本文介绍了Python如何使用同步队列在多个线程之间安全地交换数据。
Python线程安全队列介绍
内置queue
模块可以在多个线程之间安全地交换数据。queue
模块中的Queue
类实现了所有必需的锁定语义。
创建一个新队列
要创建一个新队列,可以使用Queue
如下构造函数:
from queue import Queue |
|
queue = Queue() |
要创建具有大小限制的队列,可以使用该maxsize
参数。例如,以下创建一个最多可存储 10 个项目的队列:
from queue import Queue |
|
queue = Queue(maxsize=10) |
向队列中添加项目
要将项目添加到队列中,可以使用如下put()
方法:
# ... |
|
queue.add(item) |
一旦队列已满,将无法向队列中添加项目。对该put()
方法的调用将阻塞,直到队列有可用空间为止。
如果你不希望该put()
方法在队列已满时阻塞,可以将block
参数设置为False
:
<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python">queue.put(item, block=<span style="color:#6cb6ff">False</span>)</code></span></span>
在这种情况下,如果队列已满,该put()
方法将引发queue.Full
异常:
try: |
|
queue.put(item, block=False) |
|
except queue.Full as e: |
|
# handle exceptoin |
要将项目添加到大小有限的队列并超时阻塞,可以使用如下timeout
参数:
try: |
|
queue.put(item, timeout=3) |
|
except queue.Full as e: |
|
# handle exceptoin |
从队列中获取一个项目
要从队列中获取项目,可以使用以下get()
方法:
<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python">item = queue.get()</code></span></span>
该get()
方法将阻塞,直到有一个项目可用于从队列中检索。
要在不阻塞的情况下从队列中获取项目,可以将 block 参数设置为False
:
try: |
|
queue.get(block=False) |
|
except queue.Empty: |
|
# handle exception |
要从队列中获取项目并在时间限制内阻塞,可以使用get()
带超时的方法:
try: |
|
item = queue.get(timeout=10) |
|
except queue.Empty: |
|
# ... |
获取队列的大小
该qsize()
方法返回队列中的项目数:
<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python">size = queue.size()</code></span></span>
此外,empty()
如果队列为空,则该方法返回 True,否则返回 False。另一方面,full()
如果队列已满,则该方法返回 True
,否则返回 False
。
将任务标记为已完成
添加到队列中的项目代表工作单元或任务。
当线程调用该get()
方法从队列中获取项目时,它可能需要在任务完成之前对其进行处理。
一旦完成,线程可以调用task_done()
队列的方法来表明它已经完成了任务:
item = queue.get() |
|
# process the item |
|
# ... |
|
# mark the item as completed |
|
queue.task_done() |
等待队列中的所有任务完成
要等待队列中的所有任务完成,可以调用join()
队列对象上的方法:
<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python">queue.join()</code></span></span>
Python 线程安全队列示例
下面的例子说明了如何使用线程安全队列在两个线程之间交换数据:
import time |
|
from queue import Empty, Queue |
|
from threading import Thread |
|
def producer(queue): |
|
for i in range(1, 6): |
|
print(f'Inserting item {i} into the queue') |
|
time.sleep(1) |
|
queue.put(i) |
|
def consumer(queue): |
|
while True: |
|
try: |
|
item = queue.get() |
|
except Empty: |
|
continue |
|
else: |
|
print(f'Processing item {item}') |
|
time.sleep(2) |
|
queue.task_done() |
|
def main(): |
|
queue = Queue() |
|
# create a producer thread and start it |
|
producer_thread = Thread( |
|
target=producer, |
|
args=(queue,) |
|
) |
|
producer_thread.start() |
|
# create a consumer thread and start it |
|
consumer_thread = Thread( |
|
target=consumer, |
|
args=(queue,), |
|
daemon=True |
|
) |
|
consumer_thread.start() |
|
# wait for all tasks to be added to the queue |
|
producer_thread.join() |
|
# wait for all tasks on the queue to be completed |
|
queue.join() |
|
if __name__ == '__main__': |
|
main() |
首先,定义将1 到 6 的数字添加到队列中的producer()
函数。它在每次迭代中延迟一秒钟:
def producer(queue): |
|
for i in range(1, 6): |
|
print(f'Inserting item {i} into the queue') |
|
time.sleep(1) |
|
queue.put(i) |
其次,定义consumer()
从队列中获取项目并对其进行处理的函数。它在处理队列中的每个项目后延迟两秒钟:
def consumer(queue): |
|
while True: |
|
try: |
|
item = queue.get() |
|
except Empty: |
|
continue |
|
else: |
|
print(f'Processing item {item}') |
|
time.sleep(2) |
|
queue.task_done() |
tqueue.ask_done()
表示函数已经处理了队列中的项目。
第三,定义main()
创建两个线程的函数,一个线程每隔一秒向队列添加一个数字,而另一个线程每两秒处理队列中的一个项目:
def main(): |
|
queue = Queue() |
|
# create a producer thread and start it |
|
producer_thread = Thread( |
|
target=producer, |
|
args=(queue,) |
|
) |
|
producer_thread.start() |
|
# create a consumer thread and start it |
|
consumer_thread = Thread( |
|
target=consumer, |
|
args=(queue,), |
|
daemon=True |
|
) |
|
consumer_thread.start() |
|
# wait for all tasks to be added to the queue |
|
producer_thread.join() |
|
# wait for all tasks on the queue to be completed |
|
queue.join() |
输出:
Inserting item 1 into the queue |
|
Inserting item 2 into the queue |
|
Processing item 1 |
|
Inserting item 3 into the queue |
|
Processing item 2 |
|
Inserting item 4 into the queue |
|
Inserting item 5 into the queue |
|
Processing item 3 |
|
Processing item 4 |
|
Processing item 5 |
以下是main()
函数中的步骤:
- 通过调用
Queue()
构造函数创建一个新队列 - 创建一个名为的新线程
producer_thread
并立即启动它 - 创建一个名为的守护线程
consumer_thread
并立即启动它。 join()
使用线程的方法等待所有号码加入队列。- 调用队列的
join()
方法,等待队列上的所有任务完成。
生产者每秒向队列中添加一个数字,消费者每两秒从队列中处理一个数字。它还每秒显示队列中的数字。
总结
- 使用模块的
Queue
类queue
在多个线程之间安全地交换数据。