利用队列线程间通信
前面我们在用event或者condition进行通信,但是当共享数据是列表形的时候就很麻烦,这是我们便可以利用队列:
我们创建一个能够被多线程共享的Queue
对象,然后线程使用put()get()
来操作元素,一个最简单的生产者消费者的例子:
from queue import Queue
from threading import Thread
# A thread that produces data
def producer(out_q):
while True:
# Produce some data
...
out_q.put(data)
# A thread that consumes data
def consumer(in_q):
while True:
# Get some data
data = in_q.get()
# Process the data
...
# Create the shared queue and launch both threads
q = Queue()
t1 = Thread(target=consumer, args=(q,))
t2 = Thread(target=producer, args=(q,))
t1.start()
t2.start()
Queue
对象已经包含了必要的锁,所以你可以通过它在多个线程间多安全地共享数据。 当使用队列时,协调生产者和消费者的关闭问题可能会有一些麻烦。一个通用的解决方法是在队列中放置一个特殊的值,当消费者读到这个值的时候,终止执行。例如:
from queue import Queue
from threading import Thread
# Object that signals shutdown
_sentinel = object()
# A thread that produces data
def producer(out_q):
while running:
# Produce some data
...
out_q.put(data)
# Put the sentinel on the queue to indicate completion
out_q.put(_sentinel)
# A thread that consumes data
def consumer(in_q):
while True:
# Get some data
data = in_q.get()
# Check for termination
if data is _sentinel:
in_q.put(_sentinel)##这里检查到标记之后又立刻把它放回了,这样其它线程也能够看到并停止
break
# Process the data
...
Queue中的join()与task_done()
使用队列来进行线程间通信是一个单向、不确定的过程。通常情况下,你没有办法知道接收数据的线程是什么时候接收到的数据并开始工作的。不过队列对象提供一些基本完成的特性,比如下边这个例子中的
task_done()
和join()
:
实际上就是没法判断队列什么时候清空了,而:
Queue.task_done()
在完成一项工作之后,Queue.task_done()
函数向任务已经完成的队列发送一个信号
Queue.join()
实际上意味着等到队列为空,再执行别的操作
如果线程里每从队列里取一次,但没有执行task_done(),则join无法判断队列到底有没有结束,在最后执行个join()是等不到结果的,会一直挂起。
可以理解为,每task_done一次 就从队列里删掉一个元素,这样在最后join的时候根据队列长度是否为零来判断队列是否结束,从而执行主线程。
下面这个例子,会在join()的地方无限挂起,因为join在等队列清空,但是由于没有task_done,它认为队列还没有清空,还在一直等。
重点:
那说了半天,我为啥要用join呢,直接判断那个队列是不是空不就完了么,下面几个回答
join()只看task_done(),和队列空不空其实没关系
queue.join()
会一直阻塞,直到队列中所有的message都被get出来并且调用task_done才会返回。通常用在等待所有的任务都处理完了,然后退出进程。
empty会立马返回,用你的while循环检查时,如果队列为空线程会一直用死循环。循环等待会很耗CPU。
上例子:
from queue import Queue
from threading import Thread
from time import sleep
import os
class Mythread(Thread):
def __init__(self,que:Queue):
Thread.__init__(self)
self.queue=que
self.data=0
# A thread that produces data
def producer(self):
while True:
sleep(2)
if self.queue.empty():
print('队列空了')
# Produce some data
for i in range(10):
self.data+=1
self.queue.put(self.data)
# A thread that consumes data
def consumer(self):
while True:
# Get some data
sleep(0.5)
if self.queue.empty():
print('队列空了')
data = self.queue.get()
print('消费掉了',data)
# self.queue.task_done()
if __name__ == "__main__":
# Create the shared queue and launch both threads
q = Queue()
for i in range(10):
q.put(i)
t1 = Thread(target=Mythread(q).consumer,args=())
# t2 = Thread(target=Mythread(q).producer,args=())
t1.start()
# t2.start()
# Wait for all produced items to be consumed
q.join()
sleep(2)
print('主线程结束')
os._exit(0)
在我这样 # self.queue.task_done()
的时候,就算任务完成了,线程还是不会退出的:
然后我把注释去掉,就终于可以自动退出,执行别的任务啦
哎,今天一下午就看了这么一点,终于搞懂了