我需要一个队列,多个线程可以把东西放进去,多个线程可以从中读取。

Python至少有两个队列类,queue。Queue和collections.deque,前者似乎在内部使用后者。两者在文档中都声称是线程安全的。

然而,Queue文档还声明:

Collections.deque是另一种选择 实现无界队列 使用快速原子append()和 Popleft()操作 需要锁定。

我想我不太理解:这是否意味着deque不是完全线程安全的?

如果是的话,我可能无法完全理解这两个类的区别。我可以看到Queue增加了阻塞功能。另一方面,它失去了一些deque特性,比如对in-operator的支持。

直接访问内部deque对象是

Queue().deque . x

线程安全?

另外,当deque已经是线程安全的时候,为什么Queue还为它的操作使用互斥量呢?


当前回答

Deque是线程安全的。“不需要锁定的操作”意味着你不必自己进行锁定,deque会处理它。

看一下Queue源,内部deque叫做self。queue(),并使用互斥量进行访问和突变,因此queue()。使用队列不是线程安全的。

如果您正在寻找一个“in”操作符,那么deque或queue可能不是最适合您的问题的数据结构。

其他回答

(似乎我没有声誉来评论…) 您需要注意从不同的线程中使用deque的哪些方法。

deque.get()似乎是线程安全的,但我发现这样做

for item in a_deque:
   process(item)

如果另一个线程同时添加项,则可能失败。 我得到了一个RuntimeException,抱怨“deque在迭代期间突变”。

检查collectionsmodule.c查看哪些操作受此影响

如果您所寻找的只是线程间传输对象的线程安全方法,那么这两种方法都可以工作(对于FIFO和LIFO都可以)。先进先出:

Queue.put()和Queue.get()是线程安全的 Deque.append()和deque.popleft()是线程安全的

注意:

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

有关信息,有deque线程安全的Python票据引用(https://bugs.python.org/issue15329)。 标题“阐明哪些deque方法是线程安全的”

底线在这里:https://bugs.python.org/issue15329#msg199368

deque的append(), appendleft(), pop(), popleft()和len(d) 操作在CPython中是线程安全的。附加方法有一个 decf在结尾(对于maxlen已经设置的情况),但是这个 在进行了所有结构更新之后发生 不变量已经恢复,所以可以处理这些操作 原子。

不管怎样,如果你不是100%确定,你更喜欢可靠性而不是性能,就像锁;)

队列中。Queue和collections.deque有不同的用途。队列中。Queue的目的是允许不同的线程使用排队的消息/数据进行通信,而collections.deque只是作为一个数据结构。这就是队列的原因。Queue有put_nowait()、get_nowait()和join()等方法,而collections.deque没有。队列中。Queue不打算用作集合,这就是为什么它缺乏类似于in操作符的特性。

这可以归结为:如果您有多个线程,并且希望它们能够在不需要锁的情况下进行通信,那么您将寻找Queue.Queue;如果您只想要一个队列或双端队列作为数据结构,请使用collections.deque。

最后,访问和操作Queue的内部deque。排队是在玩火——你真的不想这样做。

deque上的所有单元素方法都是原子的和线程安全的。所有其他方法也是线程安全的。比如len(dq) dq[4]会产生瞬时正确值。但是想想例如dq.extend(mylist):当其他线程也在同一侧附加元素时,你不能保证mylist中的所有元素都是在一行中归档的——但这通常不是线程间通信和被质疑任务的要求。

所以deque比Queue(在底层使用deque)快20倍,除非你不需要“舒适的”同步API(阻塞/超时),严格的maxsize服从或“覆盖这些方法(_put, _get, ..)来实现其他队列组织”子类化行为,或者当你自己处理这些事情时,那么一个纯deque对于高速线程间通信来说是一个很好的和有效的交易。

事实上,在Queue.py中大量使用额外的互斥量和额外的方法._get()等方法调用是由于向后兼容性限制、过去的过度设计以及缺乏对线程间通信中这个重要的速度瓶颈问题提供有效解决方案的关注。在较旧的Python版本中使用了列表-但即使list.append()/.pop(0)也是原子的和线程安全的…