我需要一个队列,多个线程可以把东西放进去,多个线程可以从中读取。
Python至少有两个队列类,queue。Queue和collections.deque,前者似乎在内部使用后者。两者在文档中都声称是线程安全的。
然而,Queue文档还声明:
Collections.deque是另一种选择
实现无界队列
使用快速原子append()和
Popleft()操作
需要锁定。
我想我不太理解:这是否意味着deque不是完全线程安全的?
如果是的话,我可能无法完全理解这两个类的区别。我可以看到Queue增加了阻塞功能。另一方面,它失去了一些deque特性,比如对in-operator的支持。
直接访问内部deque对象是
Queue().deque . x
线程安全?
另外,当deque已经是线程安全的时候,为什么Queue还为它的操作使用互斥量呢?
在每个deque追加和popleft中添加notify_all()会导致deque的结果比默认deque行为所获得的20倍的改善要糟糕得多:
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()
deque是无界队列的另一种实现,具有不需要锁定的快速原子append()和popleft()操作。
文档队列
如果您所寻找的只是线程间传输对象的线程安全方法,那么这两种方法都可以工作(对于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