スポンサーリンク

Pythonの並列処理を完全理解!concurrent.futuresで簡単マルチスレッド&マルチプロセス

自動化スクリプト
  1. 1. はじめに|Pythonの処理を高速化したい人へ
  2. 2. 逐次処理とは?|まずは基本からおさらい
    1. ■ 逐次処理ってなに?
    2. ■ どんなイメージ?
    3. ■ どんなときに困る?
  3. 3. 並列処理と並行処理の違いをわかりやすく解説
    1. ■ 並列処理(へいれつしょり)とは?
    2. ■ 並行処理(へいこうしょり)とは?
    3. ■ 並列 vs 並行:まとめると?
  4. 4. マルチスレッドとマルチプロセスの違いと使い分け
    1. ■ そもそも「プロセス」と「スレッド」ってなに?
    2. ■ マルチスレッドの特徴と使いどころ
      1. ✅ メリット
      2. ⚠ デメリット
      3. 💡 向いている処理(I/Oバウンド)
    3. ■ マルチプロセスの特徴と使いどころ
      1. ✅ メリット
      2. ⚠ デメリット
      3. 💡 向いている処理(CPUバウンド)
    4. ■ どっちを使えばいいの?
  5. 5. concurrent.futuresとは?|初心者にやさしい並列処理モジュール
    1. ■ concurrent.futuresってなに?
    2. ■ 2つのExecutorクラスがポイント!
    3. ■ どんな書き方をするの?
    4. ■ なにがそんなに便利なの?
  6. 6. ThreadPoolExecutorの使い方
    1. ■ 処理の流れをざっくり説明すると…
    2. ■ まずは基本のコードから
    3. ■ 実行結果のイメージ
    4. ■ map()を使うともっとスッキリ!
    5. ■ こんなときに便利!
  7. 7. ProcessPoolExecutorの使い方
    1. ■ 基本の使い方(コード例)
    2. ■ 実行結果のイメージ
    3. ■ 注意点:if name == “main” の意味
    4. ■ map()も使えます!
    5. ■ どんなときに使うの?
  8. 8. 注意点とベストプラクティス
    1. ✅ 1. GILの影響に注意しよう(マルチスレッド)
    2. ✅ 2. if __name__ == "__main__" を忘れずに(マルチプロセス)
    3. ✅ 3. max_workersの数はほどほどに
    4. ✅ 4. 戻り値を忘れずに扱おう(Future)
    5. ✅ 5. デバッグしづらいので print で確認しよう
  9. 9. まとめ
    1. 🔁 おさらいポイント
    2. 🛠 実務でも使えるシーン
    3. 🤝 初心者にもやさしい!
    4. ✅ あわせて読みたい
  10. よくある質問(Q&A)
    1. 関連投稿:

1. はじめに|Pythonの処理を高速化したい人へ

Pythonでプログラムを書いていると、

「処理が遅い…」
「for文で順番に回しているのが限界に感じる」
「待ち時間が多くて、もっと効率よくできないの?」

と感じる場面はありませんか?

たとえば、

  • 大量データを処理するバッチ処理
  • Webスクレイピングで複数ページを取得するとき
  • APIのレスポンス待ちが続く処理

こうしたケースでは、**処理を「同時に進められるかどうか」**が、実行時間を大きく左右します。

そこで役立つのが、**Pythonの「並列処理・並行処理」**です。
処理を1つずつ順番に実行するのではなく、複数のタスクを同時に扱うことで、体感できるレベルで高速化できることも珍しくありません。

とはいえ、

  • 並列処理と並行処理の違いがよく分からない
  • マルチスレッドとマルチプロセスって何が違うの?
  • 難しそうで手を出しづらい…

と感じている方も多いはずです。

安心してください。
Pythonには concurrent.futures という標準ライブラリがあり、複雑な仕組みを意識しなくても、初心者でも安全にマルチスレッド・マルチプロセスを扱えるようになっています。

この記事では、

  • 「並列処理」と「並行処理」の違い
  • マルチスレッドとマルチプロセスの使い分け
  • concurrent.futures を使った具体的な書き方

を、for文に慣れてきた人が次のステップに進めるレベル感で、やさしく解説していきます。

「Pythonをもっと速く動かしたい」
「処理時間のストレスから解放されたい」

そんな方は、ぜひ最後まで読み進めてみてください。




2. 逐次処理とは?|まずは基本からおさらい

まずは、Pythonの標準的な処理の流れである「逐次処理(ちくじしょり)」について確認しておきましょう。

■ 逐次処理ってなに?

逐次処理とは、コードが上から順番にひとつずつ実行される流れのことです。Pythonで普通にプログラムを書くと、たいていはこの逐次処理になります。

たとえば、こんなコードを見てみましょう。

print("1つ目の処理")
print("2つ目の処理")
print("3つ目の処理")

この場合、実行すると

1つ目の処理
2つ目の処理
3つ目の処理

というように、上から順に一つずつ表示されます。これが「逐次処理」です。

■ どんなイメージ?

イメージとしては、1人の作業者が1つの仕事を終えてから次の仕事に取りかかるようなものです。たとえば、

  • 料理を作る → 食器を洗う → 掃除をする

というように、一つずつ順番に片づけていく流れです。

■ どんなときに困る?

逐次処理はシンプルで分かりやすいのですが、時間がかかる作業が途中にあると、そのあとの処理が全部待たされてしまうという欠点があります。

たとえば、

  • Webサイトにアクセスしてデータを取得(5秒)
  • データを表示(1秒)

という2つの作業があると、最初の「5秒」が終わらないと次に進めません。

こういった場合、待ち時間を有効活用できたら便利ですよね。そこで登場するのが「並列処理」や「並行処理」です!




3. 並列処理と並行処理の違いをわかりやすく解説

ここからは、Pythonで処理を効率よく進めるためのキーワードである「並列処理」と「並行処理」の違いについて説明します。

この2つ、言葉は似ているけど意味と使い方がちょっと違うので、イメージでしっかり理解しておきましょう!


■ 並列処理(へいれつしょり)とは?

並列処理は、複数の作業を同時に実行することを指します。

たとえば、以下のような状況を想像してみてください。

🍳 Aさんが料理、Bさんが掃除、Cさんが洗濯を「同時に」やっている。

つまり、3人がそれぞれ別の作業を同時にしているイメージです。これが並列処理です。

Pythonでは「マルチプロセス」という仕組みを使って、この並列処理を実現します。


■ 並行処理(へいこうしょり)とは?

一方、並行処理は、1人の作業者が、うまくタイミングを調整して複数の作業を少しずつこなすことを指します。

例えば…

🧍‍♂️Aさんが、鍋を煮込んでいる間に床を掃除し、さらに洗濯機が止まるのを待ちながらアイロンがけをしている。

つまり、ひとつの作業の「待ち時間」を使って、別の作業を進めている感じです。

Pythonでは、「マルチスレッド」という仕組みを使って、この並行処理を行います。


■ 並列 vs 並行:まとめると?

分類イメージPythonで使う技術向いている処理
並列処理複数人が同時に作業マルチプロセス計算量の多い処理(CPUバウンド)
並行処理1人が作業の合間に別の仕事をするマルチスレッド待ち時間のある処理(I/Oバウンド)

次は、Pythonの「マルチスレッド」と「マルチプロセス」がそれぞれどうやって動くのか、どんな特徴があるのかを、もう少し深掘りしていきます!




4. マルチスレッドとマルチプロセスの違いと使い分け

前の章で「並列処理=マルチプロセス」「並行処理=マルチスレッド」と紹介しましたね。
では実際に、**マルチスレッドとマルチプロセスはどこが違って、どう使い分ければいいのか?**をもう少し具体的に見ていきましょう。


■ そもそも「プロセス」と「スレッド」ってなに?

まずは言葉の定義をシンプルに説明します。

用語説明
プロセスコンピューター上で動いているアプリやプログラムの単位のこと。Pythonでスクリプトを実行すると1つのプロセスになります。
スレッドプロセスの中で動く、さらに小さな実行単位。1つのプロセスの中で複数のスレッドを同時に動かすことができます。

ざっくり言えば、プロセスはアプリ本体、スレッドはその中で同時に動く作業員といったイメージです。


■ マルチスレッドの特徴と使いどころ

マルチスレッドは、1つのプロセス内で複数のスレッドを動かす処理方法です。

✅ メリット

  • メモリ消費が少ない(プロセスを増やさないから)
  • データ共有が簡単(同じメモリ空間で動くため)

⚠ デメリット

  • Pythonでは「GIL(グローバルインタプリタロック)」という仕組みがあるため、CPUをたくさん使う処理にはあまり向いていません

💡 向いている処理(I/Oバウンド)

  • ネットワーク通信(APIアクセス、Webクローリングなど)
  • ファイル読み書き
  • データベースとのやりとり など

■ マルチプロセスの特徴と使いどころ

マルチプロセスは、複数のPythonプロセスを立ち上げて同時に処理を進める方法です。

✅ メリット

  • 各プロセスが独立しているので、GILの制限を受けずにCPUをフル活用できる
  • 重たい計算処理に強い

⚠ デメリット

  • メモリを多く消費する
  • プロセス間でのデータ共有がやや面倒

💡 向いている処理(CPUバウンド)

  • 数値計算(例:画像処理、機械学習の前処理など)
  • シミュレーション処理
  • 複雑なアルゴリズムの計算 など

■ どっちを使えばいいの?

簡単な目安としては以下のように考えましょう。

処理のタイプおすすめ
待ち時間が多い処理(I/Oバウンド)マルチスレッド(ThreadPoolExecutor
CPUをたくさん使う処理(CPUバウンド)マルチプロセス(ProcessPoolExecutor

次は、実際にPythonコードで「concurrent.futures」を使ってマルチスレッドを実装してみましょう!




5. concurrent.futuresとは?|初心者にやさしい並列処理モジュール

ここまで読んで、「マルチスレッドとマルチプロセスって便利そう!」と思った方も多いのではないでしょうか?
でも実際にコードで使おうとすると、threadingmultiprocessingモジュールは、ちょっとややこしい構文が多くて混乱しやすいんですよね。

そんなときに使いたいのが、**concurrent.futures**モジュールです!


■ concurrent.futuresってなに?

concurrent.futuresは、Python 3.2から使える標準モジュールで、
マルチスレッド」と「マルチプロセス」の両方をほぼ同じ書き方で扱えるという、超便利な仕組みです。

むずかしい用語を覚えなくても、たった数行で並列・並行処理ができちゃいます!


■ 2つのExecutorクラスがポイント!

このモジュールの主役は、次の2つのクラスです:

クラス名内容
ThreadPoolExecutorスレッドを使った並行処理
ProcessPoolExecutorプロセスを使った並列処理

どちらも「Executor(実行者)」という名前の通り、処理を並べて実行してくれる便利屋さんのような存在です。


■ どんな書き方をするの?

基本の構文はとってもシンプルです:

from concurrent.futures import ThreadPoolExecutor

def my_task(n):
# 何らかの処理
return n * 2

with ThreadPoolExecutor(max_workers=2) as executor:
future = executor.submit(my_task, 10)
result = future.result()
print(result) # → 20

このように、

  • submit()で関数を非同期に実行
  • result()で結果を受け取る

という流れが基本です。
さらに、複数のデータを一気に処理したいときはmap()関数が使えます。


■ なにがそんなに便利なの?

  1. 同じ書き方でスレッドもプロセスも切り替えられる
  2. with構文でリソース管理がラク
  3. 直感的に書けて初心者にもわかりやすい

「まずは処理を速くしたい!」という人にとって、ベストな入門ツールです!




6. ThreadPoolExecutorの使い方

ここからは、実際にPythonでスレッドによる並行処理を実装してみましょう。使うのはもちろん、concurrent.futuresの中の**ThreadPoolExecutor**です。


■ 処理の流れをざっくり説明すると…

  1. 実行したい関数を用意する
  2. ThreadPoolExecutorで「作業者」を用意する
  3. submit()map()で関数を並行実行する
  4. result()で結果を受け取る

この4ステップだけでOK!


■ まずは基本のコードから

from concurrent.futures import ThreadPoolExecutor
import time

# 処理する関数
def slow_task(n):
print(f"処理中: {n}")
time.sleep(2)
return n * 10

# スレッドで並行実行
with ThreadPoolExecutor(max_workers=2) as executor:
future1 = executor.submit(slow_task, 1)
future2 = executor.submit(slow_task, 2)

# 処理の完了を待って結果を取得
result1 = future1.result()
result2 = future2.result()

print("結果1:", result1)
print("結果2:", result2)

■ 実行結果のイメージ

処理中: 1
処理中: 2
(2秒後)
結果1: 10
結果2: 20

ここでは「max_workers=2」としているので、2つの処理が同時に動いているのがわかります。

もしmax_workers=1だったら、1つずつ順番に動く「逐次処理」になってしまいます。


■ map()を使うともっとスッキリ!

同じ関数を複数の引数で処理したいなら、map()を使うととてもスマートです。

with ThreadPoolExecutor(max_workers=3) as executor:
results = executor.map(slow_task, [1, 2, 3])
for r in results:
print("結果:", r)

これだけで、3つのタスクを並行で実行し、順に結果を受け取れます。


■ こんなときに便利!

  • 複数のWeb APIにアクセスするとき
  • 複数のファイルを読み書きするとき
  • 複数の画像やPDFを処理するとき

I/Oが多い場面では、スレッドで**「待っている間に別の作業」**をするだけで、処理時間が大きく短縮できます。




7. ProcessPoolExecutorの使い方

前の章では、スレッドを使って「待ち時間のある処理(I/Oバウンド)」を並行して実行する方法を紹介しました。
今度は、複数のプロセスを使って計算を速くするProcessPoolExecutor」の使い方を見ていきましょう!

実は…書き方はほぼ同じ!
ThreadPoolExecutorProcessPoolExecutorに変えるだけでOKなんです!


■ 基本の使い方(コード例)

from concurrent.futures import ProcessPoolExecutor
import time

# CPUを使う処理(たとえば重たい計算)
def heavy_task(n):
print(f"処理中: {n}")
time.sleep(2)
return n * n

# プロセスで並列実行
if __name__ == "__main__":
with ProcessPoolExecutor(max_workers=2) as executor:
future1 = executor.submit(heavy_task, 3)
future2 = executor.submit(heavy_task, 5)

result1 = future1.result()
result2 = future2.result()

print("結果1:", result1)
print("結果2:", result2)

■ 実行結果のイメージ

処理中: 3
処理中: 5
(2秒後)
結果1: 9
結果2: 25

この例でも、「max_workers=2」としているので、2つのプロセスが並列に動いて処理時間が短くなっています。


■ 注意点:if name == “main” の意味

WindowsやMacでこのコードを動かすときは、必ず次のように書いてください👇

if __name__ == "__main__":
# ここに処理を書く

これは、「このファイルが直接実行されたときだけ中身を動かす」というPythonの決まりです。
マルチプロセスでこれを書かないと、無限にプロセスが増えるバグが起きることもあるので要注意です!


■ map()も使えます!

複数の引数に対してまとめて処理したい場合は、map()も使えます。

if __name__ == "__main__":
with ProcessPoolExecutor(max_workers=3) as executor:
results = executor.map(heavy_task, [1, 2, 3])
for r in results:
print("結果:", r)

■ どんなときに使うの?

  • 画像や動画などの重いファイルを一括処理したいとき
  • 数値計算が多い処理を高速化したいとき
  • AIモデルや統計計算の前処理 など

とにかく「CPUをよく使う処理」に強いのが、マルチプロセスの魅力です。




8. 注意点とベストプラクティス

ここまでで、Pythonでマルチスレッド(ThreadPoolExecutor)とマルチプロセス(ProcessPoolExecutor)を使って、処理を効率よく並列・並行に実行する方法を学びました。

でも、実際のプログラムに取り入れる前に、知っておくとトラブルを防げるポイントがあります。


✅ 1. GILの影響に注意しよう(マルチスレッド)

Pythonには「**GIL(グローバルインタプリタロック)」**という仕組みがあります。

これは、1つのPythonプロセスで同時に複数のスレッドが実行されないように制限をかける仕組みです。

  • **I/Oが多い処理(API、ファイル読み書き)**には問題ありません。
  • でも、CPUをたくさん使う重たい処理だと、スレッドを増やしてもあまり速くなりません。

そういうときは、**ProcessPoolExecutor(マルチプロセス)**のほうが効果的です。


✅ 2. if __name__ == "__main__" を忘れずに(マルチプロセス)

マルチプロセスでコードを書くときは、必ずこのブロックで実行部分を囲みましょう。

if __name__ == "__main__":
# 並列処理のコードはここに書く

これは特にWindowsやMacで重要です。これを忘れると、プログラムが予期せず再帰的に自分を呼び出して、無限ループやクラッシュの原因になります


✅ 3. max_workersの数はほどほどに

max_workersでワーカー数(同時に動かす処理の数)を指定できますが、多すぎると逆に遅くなることがあります。

  • 目安は、マルチプロセスなら「CPUのコア数」、マルチスレッドなら「処理の待ち時間に応じて」
  • コア数は、次のコードで確認できます:
import os
print(os.cpu_count()) # 例: 8

✅ 4. 戻り値を忘れずに扱おう(Future)

submit()で実行した処理は、Futureオブジェクトとして返ってきます。これをちゃんとresult()で受け取らないと、処理が終わったかどうかわからないまま次に進んでしまうことがあります。

future = executor.submit(task, 10)
result = future.result() # 処理の完了を待って結果を取得

✅ 5. デバッグしづらいので print で確認しよう

並列・並行処理では、処理の順番がバラバラになることがあります。
そのため、どの処理が今どこで動いているのか、print文でログを出しておくと安心です。

print(f"タスク{n}を開始します")

以上のポイントを押さえておけば、concurrent.futuresを使った並列・並行処理を安全かつ効果的に実装できます!




9. まとめ

今回は、Pythonの処理を効率化・高速化する方法として「並列処理と並行処理」、そしてそれをシンプルに実装できる「concurrent.futuresモジュール」について解説してきました。


🔁 おさらいポイント

  • 逐次処理は、処理を1つずつ順番に実行するやり方。簡単だけど待ち時間がネックになる。
  • **並列処理(マルチプロセス)**は、複数のプロセスを同時に動かして、**計算が重い処理(CPUバウンド)**を速くする方法。
  • **並行処理(マルチスレッド)**は、待ち時間の合間に他の作業を進めて、**I/O待ちの処理(APIやファイル操作)**を効率よくこなす方法。
  • concurrent.futuresを使えば、どちらの方式も同じような書き方で簡単に実装できる

🛠 実務でも使えるシーン

  • Webスクレイピングで複数ページを同時取得
  • 複数のファイルを並行して読み込み・変換
  • 複雑な数値処理をマルチプロセスで高速化
  • バックグラウンド処理を簡単に導入 など

🤝 初心者にもやさしい!

難しそうに思える「並列・並行処理」も、concurrent.futuresを使えばたった数行のコードで実現できます。

最初は小さな処理からでもいいので、ぜひ実際に試してみてください。
「時間がかかっていたあの処理、こんなに速くなるのか!」ときっと驚くはずです。


✅ あわせて読みたい

以下の記事もあわせて読むことで、Pythonの処理効率や実用スキルをさらに高めることができます!


よくある質問(Q&A)

Q
並列処理と並行処理の違いって何ですか?
A

並列処理は「複数の作業をまったく同時に実行する」ことで、主にマルチプロセスで実現されます。並行処理は「1人が交互に複数の作業をこなす」イメージで、マルチスレッドで実現されます。処理の性質によって使い分けましょう。

Q
ThreadPoolExecutorとProcessPoolExecutor、どっちを使えばいいの?
A

**待ち時間の多い処理(I/Oバウンド)**にはThreadPoolExecutor

**計算量の多い処理(CPUバウンド)**にはProcessPoolExecutor
が向いています。

Q
submit()map()はどう違うんですか?
A

submit()1つずつ処理を追加できる方法で、Futureを使って戻り値を受け取れます。

map()リストや複数の値を一括で処理するのに向いていて、簡潔に書けます。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク