web-dev-qa-db-ja.com

Queue.Queue vs. collections.deque

複数のスレッドが何かを入れることができ、複数のスレッドが読み込むことができるキューが必要です。

Pythonには少なくともQueue.Queueとcollections.dequeの2つのキュークラスがあり、前者は後者を内部的に使用しているようです。どちらもドキュメントではスレッドセーフであると主張しています。

ただし、キュードキュメントには次のようにも記載されています。

collections.dequeは、ロックを必要としない高速アトミックappend()およびpopleft()操作を備えた無制限キューの代替実装です。

私はまったく理解していないと思います:これは、dequeが結局完全にスレッドセーフではないことを意味しますか?

そうであれば、2つのクラスの違いを完全に理解できない可能性があります。 Queueがブロッキング機能を追加していることがわかります。一方、操作中のサポートのようないくつかのdeque機能は失われます。

内部dequeオブジェクトに直接アクセスするには、

x in Queue()。deque

スレッドセーフ?

また、dequeがすでにスレッドセーフであるときに、Queueがその操作にミューテックスを使用するのはなぜですか?

151
miracle2k

_Queue.Queue_と_collections.deque_は異なる目的を果たします。 Queue.Queueは、キューに入れられたメッセージ/データを使用してさまざまなスレッドが通信できるようにすることを目的としていますが、_collections.deque_は単にデータ構造を目的としています。 _Queue.Queue_にはput_nowait()get_nowait()、およびjoin()のようなメソッドがありますが、_collections.deque_にはありません。 _Queue.Queue_はコレクションとして使用することを目的としていないため、in演算子のようなものが欠けています。

つまり、複数のスレッドがあり、ロックを必要とせずに通信できるようにしたい場合は、_Queue.Queue_を探しています。データ構造としてキューまたはダブルエンドキューが必要な場合は、_collections.deque_を使用します。

最後に、_Queue.Queue_の内部dequeにアクセスして操作するのは火で遊んでいます-あなたは本当にそれをしたくありません。

244
Keith Gaughan

あなたが探しているのがスレッド間でオブジェクトを転送するスレッドセーフな方法である場合、両方が機能します(FIFO and LIFO)。FIFOの場合:

注意:

  • dequeに対する他の操作はスレッドセーフではないかもしれませんが、わかりません。
  • dequepop()またはpopleft()でブロックしないので、新しいアイテムが到着するまで、ブロックに基づいてコンシューマスレッドフローを作成することはできません。

ただし、dequeには大幅な効率上の利点があるようです。 10万個のアイテムの挿入と削除にCPython 2.7.3を使用した数秒でのベンチマーク結果を次に示します

deque 0.0747888759791
Queue 1.60079066852

ベンチマークコードは次のとおりです。

import time
import Queue
import collections

q = collections.deque()
t0 = time.clock()
for i in xrange(100000):
    q.append(1)
for i in xrange(100000):
    q.popleft()
print 'deque', time.clock() - t0

q = Queue.Queue(200000)
t0 = time.clock()
for i in xrange(100000):
    q.put(1)
for i in xrange(100000):
    q.get()
print 'Queue', time.clock() - t0
38
Jonathan

詳細については、Python dequeスレッドセーフ用に参照されるチケット( https://bugs.python.org/issue15329 )です。スレッドセーフ」

ここの一番下の行: https://bugs.python.org/issue15329#msg199368

Dequeのappend()、appendleft()、pop()、popleft()、およびlen(d)操作は、CPythonではスレッドセーフです。 appendメソッドの最後にはDECREFがあります(maxlenが設定されている場合)が、これはすべての構造の更新が行われ、不変式が復元された後に発生するため、これらの操作をアトミックとして扱うことは問題ありません。

とにかく、100%確信が持てず、パフォーマンスよりも信頼性を好む場合は、Lockのように入力してください;)

6
BadWolf

dequeのすべての単一要素メソッドは、アトミックでスレッドセーフです。他のすべてのメソッドもスレッドセーフです。 len(dq)、_dq[4]_のようなものは、一時的に正しい値を生成します。しかし、例えばdq.extend(mylist)について:他のスレッドも同じ側に要素を追加する場合、mylistのすべての要素が連続してファイルされるという保証はありませんが、通常interの要件ではありません-スレッド通信および問題のあるタスク用。

したがって、dequeQueue(フードの下でdequeを使用)よりも20倍高速であり、「快適な」同期API(ブロッキング/タイムアウト)を必要としない限り)、厳密なmaxsize順守または「これらのメソッド(_put、_get、..)をオーバーライドして他のキュー組織を実装する」サブクラス化動作、またはそのような場合は自分で物事を行う場合、裸のdequeは、高速なスレッド間通信のための優れた効率的な取引です。

実際、_Queue.py_で余分なmutexおよび余分なメソッド._get()などのメソッド呼び出しが頻繁に使用されるのは、後方互換性の制約、過去の過剰設計、およびスレッド間通信におけるこの重要な速度のボトルネックの問題。リストは古いPythonバージョンで使用されていましたが、list.append()/。pop(0)でさえもアトミックでスレッドセーフです...

4
kxr
_deque 0.469802
Queue 0.667279
_

@Jonathanは彼のコードを少し修正し、cPython 3.6.2を使用してベンチマークを取得し、dequeループに条件を追加して、Queueの動作をシミュレートします。

_import time
from queue import Queue
import threading
import collections

mutex = threading.Lock()
condition = threading.Condition(mutex)
q = collections.deque()
t0 = time.clock()
for i in range(100000):
    with condition:
        q.append(1)
        condition.notify_all()
for _ in range(100000):
    with condition:
        q.popleft()
        condition.notify_all()
print('deque', time.clock() - t0)

q = Queue(200000)
t0 = time.clock()
for _ in range(100000):
    q.put(1)
for _ in range(100000):
    q.get()
print('Queue', time.clock() - t0)
_

そして、この関数condition.notify_all()によってパフォーマンスが制限されるようです

collections.dequeは、ロックを必要としない高速アトミックappend()およびpopleft()操作を備えた無制限キューの代替実装です。 ドキュメントキュー

3
nikan1996

(私はコメントする評判がないようです...)あなたは異なるスレッドから使​​用するdequeのメソッドに注意する必要があります。

deque.get()はスレッドセーフであるように見えますが、

for item in a_deque:
   process(item)

別のスレッドが同時にアイテムを追加している場合、失敗する可能性があります。 「deque mutation during iteration」と文句を言うRuntimeExceptionを受け取りました。

collectionsmodule.c をチェックして、この操作の影響を受ける操作を確認します

dequeはスレッドセーフです。 「ロックを必要としない操作」とは、ロックを自分で行う必要がないことを意味し、dequeがそれを処理します。

Queueソースを見ると、内部両端キューは_self.queue_と呼ばれ、アクセサとミューテーションにミューテックスを使用するため、Queue().queuenot threadです-安全に使用できます。

「in」演算子を探している場合、dequeまたはキューは問題に最適なデータ構造ではない可能性があります。

2
brian-brazil