226

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

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

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

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

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

もしそうなら、私は 2 つのクラスの違いを完全には理解していないかもしれません。Queue がブロッキング機能を追加していることがわかります。一方、インオペレーターのサポートなど、いくつかの deque 機能が失われます。

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

x in Queue().deque

スレッドセーフ?

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

4

7 に答える 7

347

Queue.Queuecollections.dequeさまざまな目的を果たします。Queue.Queueは、キューに入れられたメッセージ/データを使用してさまざまなスレッドが通信できるようにすることを目的としていますcollections.dequeが、単にデータ構造として意図されています。そのため、、、、などのメソッドQueue.Queueがありますが、ありません。コレクションとして使用することを意図していないため、オペレーターのようなものが欠けています。put_nowait()get_nowait()join()collections.dequeQueue.Queuein

Queue.Queueつまり、複数のスレッドがあり、それらがロックを必要とせずに通信できるようにしたい場合は、 ;を探しています。データ構造としてキューまたは両端キューが必要な場合は、を使用しますcollections.deque

最後に、aの内部両端キューにアクセスして操作することQueue.Queueは、火遊びです-あなたは本当にそれをしたくありません。

于 2009-04-04T15:26:29.343 に答える
58

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

ノート:

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

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

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
于 2013-12-02T14:20:14.690 に答える
12

詳細については、deque thread-safety について参照されている Python チケットがあります ( https://bugs.python.org/issue15329 )。タイトル「どの deque メソッドがスレッドセーフかを明確にする」

要点: https://bugs.python.org/issue15329#msg199368

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

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

于 2015-12-12T11:41:50.660 に答える
6

notify_all()それぞれに追加するdeque append、デフォルトの動作で達成される 20 倍の改善よりもpopleftはるかに悪い結果になります。dequedeque

deque + notify_all: 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() 操作を備えた無制限キューの代替実装です。 ドキュメント キュー

于 2017-12-25T18:07:35.950 に答える
2

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

ソースを見るとQueue、内部両端キューが呼び出されself.queue、アクセサーとミューテックスにミューテックスを使用しているため、スレッドセーフでQueue().queueはありません。

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

于 2009-04-04T14:42:23.863 に答える
1

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

deque.get() はスレッドセーフのように見えますが、実行していることがわかった

for item in a_deque:
   process(item)

別のスレッドが同時にアイテムを追加している場合、失敗する可能性があります。「反復中に deque が変更された」と不平を言う RuntimeException を受け取りました。

collectionsmodule.cをチェックして、この影響を受ける操作を確認してください

于 2015-07-28T09:31:09.400 に答える