Pythonのマルチプロセシングとマルチスレッドの違いとは?

公開日: 2025-10-11

プログラムを書いていると、「もっと処理を速くしたい」と思う瞬間は必ずあります。 特にデータ処理やスクレイピング、画像解析のように時間がかかる処理をしていると、「この時間をなんとか短縮できないか?」と考えますよね?

そんなときに出てくるのが「マルチスレッド」や「マルチプロセス(マルチプロセシング)」という言葉です。 Pythonをある程度触ってきた方なら、一度は耳にしたことがあるでしょう。

でも実際のところ、

「マルチスレッドとマルチプロセスって何が違うの?」 「どう使い分ければいいの?」

と聞かれると、すぐに答えられる人は意外と少ないんです。

この記事では、エンジニア歴10年の私が、初心者でも理解できるように、Pythonのマルチスレッドとマルチプロセスの違いと使い分け方をやさしく解説します。

マルチスレッドとマルチプロセスとは?

まずは基本的な概念から整理しましょう。

「並行処理(複数の処理を同時に進めること)」と聞くと、なんとなく「一度にいくつものことをこなせる仕組み」だと感じるかもしれません。 でも実際には、マルチスレッドマルチプロセスでは、その“同時に動く”仕組みがまったく違うのです。

プロセスとは?

プロセスとは、実行中のプログラムそのものを指します。
たとえば、あなたがPythonスクリプトをターミナルで動かしたとき、そのスクリプトが動いている1つの“箱”のような存在がプロセスです。

このプロセスは、メモリ空間(変数やデータを置く場所)やファイルハンドル、ネットワーク接続などをすべて自分専用に持っています。 つまり、他のプロセスとは完全に独立して動いているのです。

もし複数のPythonプログラムを同時に起動したとすれば、それぞれが独立したプロセスになります。1つのプロセスが落ちても、他のプロセスには影響しません。この独立性がマルチプロセスの大きな特徴です。

スレッドとは?

一方でスレッドは、プロセスの中で動く“作業の流れ”の単位です。

1つのプロセスの中には、最低でも1つのスレッド(メインスレッド)が存在します。 そのスレッドが「コードを1行ずつ実行している」イメージです。

マルチスレッドとは、そのプロセスの中で複数のスレッドを同時に走らせるということ。 つまり1つのプログラムの中で、いくつもの“作業員”が同じメモリ空間を共有しながら動いているような状態です。

スレッド同士は同じデータや変数にアクセスできるので、情報共有がとても速く行えます。
ただし、複数のスレッドが同じ変数を同時に書き換えると、値が壊れてしまうこともあります(これをレースコンディションと呼びます)。
このため、スレッドを扱う際は注意が必要です。

工場のイメージでたとえると…

この関係をわかりやすくたとえるなら、次のようになります。

  • プロセス:工場の建物全体 → それぞれが独立した工場。電気・人員・資材は別々。
  • スレッド:その中で働く人たち → 同じ工場の中で、同じ倉庫(メモリ)を共有しながら、別々の仕事を同時に進めている。

マルチスレッドは、1つの工場の中で多くの人が手分けして作業するイメージです。 マルチプロセスは、まったく別の工場をいくつも建てて、それぞれで同じ製品を並行して作るようなイメージです。

並列処理の仕組みの違い

それぞれの違いをまとめてみました。

比較項目 マルチスレッド マルチプロセス
動作単位 1つのプロセス内で複数スレッドが動く 複数のプロセスを起動して動かす
メモリ空間 共有(同じメモリを使う) 独立(プロセスごとに別のメモリ)
データ共有 簡単(同じ変数にアクセス可能) やや面倒(QueueやPipeを使う)
安全性 低い(共有データが壊れるリスク) 高い(プロセス間は独立)
起動コスト 低い(軽量) 高い(プロセスを複数立ち上げる)
適した処理 I/O処理(ネットワーク・ファイル) CPU負荷の高い計算処理

実際のイメージで考えるとわかりやすい

私の体験から言うと、最初にこの違いを意識したのはWebスクレイピングのときでした。

数百ページのサイトをクロールするスクリプトを書いていたのですが、1ページずつ順番にリクエストを送っていると、処理が全然終わらない。 「これ、同時にアクセスできないの?」と考えて出会ったのがマルチスレッドです。

マルチスレッドを使うと、ネットワーク通信のような「待ち時間の長い処理」を同時に進められるので、体感で数倍速くなりました。 一方で、画像解析のような「CPUをフルで使う処理」ではスレッドを増やしても遅いままで、「なんで?」と思ったんです。

その理由が、Python特有の仕組みGIL(グローバルインタプリタロック)でした。

GIL(グローバルインタプリタロック)とは?

Pythonには「GIL(Global Interpreter Lock)」という特徴的な制約があります。 これは、1つのPythonプロセス内で同時に動けるスレッドは常に1つだけ、というルールです。

つまり、スレッドをいくら増やしても、実際には1つずつ順番に動いている状態になります。 ですので、CPUをフルで使うような処理ではマルチスレッドの効果が出ないのです。

ただし、I/O(ネットワークやファイル読み書き)で「待ち時間」が多い処理では、この制約の影響を受けにくく、スレッドを使うことで実行効率が上がります。

マルチスレッドの例:Webスクレイピングを速くする

それでは、実際のサンプルを見てみましょう。
ここでは、複数のURLから同時にデータを取得する例を示します。

import threading
import time
import requests

urls = [
    "https://example.com",
    "https://httpbin.org/delay/2",
    "https://jsonplaceholder.typicode.com/posts/1",
]

def fetch(url):
    print(f"Fetching {url}")
    response = requests.get(url)
    print(f"Done: {url} ({len(response.text)} bytes)")

threads = []
for url in urls:
    t = threading.Thread(target=fetch, args=(url,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print("All done!")

もしこれを普通のforループで実行した場合、3つのURLを順に処理するので、トータルで6秒以上かかります。 しかしマルチスレッドを使うと、ほぼ3つを同時に実行できるので、体感では2秒程度で終わります。

スレッドは軽量で、「I/Oの待ち時間が多い処理」に向いているのです。

マルチプロセスの例:CPUを使い切る計算を速くする

一方で、CPUをフルで使うような処理、たとえば重い数値計算や画像処理の場合は、マルチスレッドではほとんどスピードアップしません。 そこで使うのがマルチプロセスです。

Pythonでは、multiprocessing モジュールを使って簡単に並列処理ができます。

from multiprocessing import Process
import math
import time

def heavy_task(n):
    print(f"Processing {n}")
    result = sum(math.sqrt(i) for i in range(10_000_000))
    print(f"Done {n}")

start = time.time()

processes = []
for i in range(4):
    p = Process(target=heavy_task, args=(i,))
    processes.append(p)
    p.start()

for p in processes:
    p.join()

print(f"Finished in {time.time() - start:.2f} seconds")

このコードでは、4つのプロセスを同時に立ち上げて、重い計算を並行して実行しています。 CPUコアが4つある場合、ほぼ4倍速くなることも珍しくありません。

どう使い分ければいいのか?

ここで改めて整理しておきましょう。

処理の種類 おすすめの手法 理由
ネットワーク通信、ファイルI/O マルチスレッド 待ち時間を有効活用できる
CPUを使う重い処理 マルチプロセス GILの制約を回避できる
軽い処理を大量に同時実行 スレッドプール(ThreadPoolExecutor) 管理が簡単
大規模なデータ処理 プロセスプール(ProcessPoolExecutor) 安全で効率的

よくある落とし穴

もちろん、マルチスレッドもマルチプロセスも「使えば速くなる」わけではありません。 むしろ間違った使い方をすると、逆に遅くなったり、バグが増えたりします。

  • スレッドの競合:同じ変数を複数スレッドが同時に触ると、値が壊れる(レースコンディション)。
  • プロセス間通信のコスト:プロセス間で大量のデータを渡すと遅くなる。
  • デバッグが難しい:並行処理は順番が一定ではないため、再現しづらいバグが出る。

特にスレッドの同期(LockやEventなど)を使い始めると、初心者は混乱しがちです。 最初はconcurrent.futuresThreadPoolExecutorProcessPoolExecutorを使うのが安全でおすすめです。

まとめ:マルチスレッドとマルチプロセスの「得意分野」を見極めよう

並行処理は「とりあえず速くなる魔法」ではありません。 それぞれの仕組みを理解し、適切な場面で使うことで、はじめて力を発揮します。

  • マルチスレッド:待ち時間の多いI/O処理に強い
  • マルチプロセス:CPU負荷の高い計算に強い

この違いを意識して設計できるようになると、Pythonのプログラムは一気にレベルアップします。 最初はちょっと難しく感じるかもしれませんが、「どっちを使うか迷ったら、まずスレッドで試して、重ければプロセスに切り替える」くらいの気持ちでOKです。

エンジニアとして10年やってきた私の経験上も、完璧に理屈を理解する前に実際に動かしてみることが一番の近道でした。 ぜひこの記事を参考に、自分の環境でも試してみてください。きっと新しい発見があるはずです。

ここまでお読みいただきありがとうございました!

Pythonの基礎から応用まで学べる
Python WebAcademy

Python WebAcademyでは、Pythonの基礎からアーキテクチャなどの応用的な内容まで幅広く学べます。
また、ブラウザ上で直接Pythonコードを試すことができ、実践的なスキルを身につけることが可能です。

Pythonの学習を始める