Python queueとは?スレッド間で安全にデータを渡す方法
queueは、スレッド間でデータを安全に受け渡すための標準モジュールです。
内部でロックを持っているため、自分でLockを書かなくても競合状態が起きません。
queueの基本
import queue
q = queue.Queue()
q.put("a")
q.put("b")
print("qsize:", q.qsize(), "empty:", q.empty())
print("get:", q.get(), q.get())
print("空になった:", q.empty())
実行結果:
qsize: 2 empty: False
get: a b
空になった: True
put()で入れてget()で取り出す——先に入れたものが先に出てきます(FIFO)。
なぜリストではだめなのか
listでもappend()とpop(0)で同じことはできます。しかし問題が2つあります。
1つ目は安全性です。「空か確認してから取り出す」という2段階の処理は、その隙に他のスレッドが横取りできます。
if not items: # ← この直後に他スレッドが取ってしまうと
return
item = items.pop(0) # ← IndexError
Queue.get()なら確認と取り出しが一体なので、この隙間がありません。
2つ目は「待てる」ことです。リストには「データが来るまで待つ」機能がありません。
空のときは待つ
q = queue.Queue()
q.get(timeout=0.2)
実行結果:
queue.Empty
get()はデータが来るまでブロックします。これがqueueの核心です。
ワーカースレッドはwhile True: q.get()と書くだけで、仕事が来るまで待機し、来たら即座に動き出します。CPUを無駄に使いません。
| 書き方 | 動き |
|---|---|
q.get() |
来るまで永久に待つ |
q.get(timeout=1) |
1秒待ってqueue.Empty |
q.get_nowait() |
待たずにqueue.Empty |
詳しくは「Python Queueのput()とget()とは?データの出し入れと待機の仕組み」で解説しています。
maxsizeで流量を制限する
q = queue.Queue(maxsize=2)
q.put(1)
q.put(2)
print("full:", q.full())
q.put(3, timeout=0.2)
実行結果:
full: True
queue.Full
maxsizeを指定すると、いっぱいのときはput()側が待たされます。
これは生産者が速すぎて消費者が追いつかないときに効きます。制限しないとキューが際限なく伸び、メモリを食い潰します。
生産者・消費者パターン
import queue
import threading
import time
q = queue.Queue()
def worker(name):
while True:
item = q.get()
if item is None:
q.task_done()
break
print(f"{name} 処理 {item}")
time.sleep (0.05)
q.task_done()
ws = [threading.Thread(target=worker, args=(f"W{i}",)) for i in range(2)]
for w in ws:
w.start()
for i in range(6):
q.put(i)
q.join()
print("全タスク完了")
for _ in ws:
q.put(None)
for w in ws:
w.join()
実行結果:
W0 処理 0
W1 処理 1
W0 処理 2
W1 処理 3
W0 処理 4
W1 処理 5
全タスク完了
これがqueueの代表的な使い方です。
- メインスレッドが仕事を
put()する - ワーカーが
get()して処理する q.join()で全部終わるまで待つNoneを送ってワーカーを終了させる
最後のNoneは「もう仕事はない」という合図で、番兵(sentinel)と呼ばれます。ワーカーの数だけ送る点に注意してください。
join()とtask_done()は「Python Queueのtask_done()とjoin()とは?全タスク完了を待つ方法」で解説しています。
3種類のキュー
lq = queue.LifoQueue()
for x in [1, 2, 3]:
lq.put(x)
print("Lifo:", [lq.get() for _ in range(3)])
pq = queue.PriorityQueue()
for x in [(3, "低"), (1, "高"), (2, "中")]:
pq.put(x)
print("Priority:", [pq.get() for _ in range(3)])
実行結果:
Lifo: [3, 2, 1]
Priority: [(1, '高'), (2, '中'), (3, '低')]
| クラス | 取り出す順 |
|---|---|
Queue |
入れた順(FIFO) |
LifoQueue |
後入れ先出し(スタック) |
PriorityQueue |
小さい値から |
SimpleQueue |
FIFO・機能を絞って高速 |
PriorityQueueには(優先度, データ)のタプルを入れます。数値が小さいほど先に出ます。
プロセス間では使えない
queue.Queueはスレッド間専用です。プロセス間では共有できません。
マルチプロセスで使うならmultiprocessing.Queueを選んでください。名前は似ていますが別物です。
「マルチプロセスとは?マルチスレッドとの違いを初心者向けに解説」も参照してください。
非同期処理(asyncio)でも同様に専用のキューが必要です。「Python asyncio.Queueとは?非同期処理でデータを受け渡す方法」を参照してください。
queueのまとめ
- スレッド間でデータを渡す——ロック不要で安全
get()はデータが来るまで待つmaxsizeで流量を制限できる- 終了の合図は
Noneなどの番兵を使う - FIFO・LIFO・優先度付きの3種類
- プロセス間には使えない


