Python在线程中使用安全队列

本文介绍了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()函数中的步骤:

  1. 通过调用Queue()构造函数创建一个新队列
  2. 创建一个名为的新线程producer_thread并立即启动它
  3. 创建一个名为的守护线程consumer_thread并立即启动它。
  4. join()使用线程的方法等待所有号码加入队列。
  5. 调用队列的join()方法,等待队列上的所有任务完成。

生产者每秒向队列中添加一个数字,消费者每两秒从队列中处理一个数字。它还每秒显示队列中的数字。

总结

  • 使用模块的Queuequeue在多个线程之间安全地交换数据。

猜你喜欢

转载自blog.csdn.net/qq_41221596/article/details/131616908