Python - 线程间通信

大家好,未来的 Python 大师们!今天,我们将开始一段激动人心的旅程,深入了解 Python 中的线程间通信。如果你是编程新手,也不用担心——我会作为你的友好向导,一步一步地解释。那么,让我们开始吧!

Python - Inter-thread Communication

什么是线程间通信?

在我们深入研究细节之前,先来了解一下线程间通信是什么。想象一下,你在一个团队中工作,负责一个大型项目。你们都在处理不同的部分,但有时你需要分享信息或者协调工作。程序中的线程也是如此,线程间通信就是它们彼此“交谈”的方式。

事件对象

让我们从线程通信的最简单方式开始:事件对象。

什么是事件对象?

事件对象就像一个可以设置或清除的标志。线程可以等待这个标志被设置后再继续执行。这有点像在过马路之前等待绿灯。

如何使用事件对象

来看一个简单的例子:

import threading
import time

def waiter(event):
print("服务员:我在等待事件被设置...")
event.wait()
print("服务员:事件被设置了!我现在可以继续了。")

def setter(event):
print("设置者:我将在3秒后设置事件...")
time.sleep(3)
event.set()
print("设置者:我已经设置了事件!")

# 创建一个事件对象
e = threading.Event()

# 创建并启动线程
t1 = threading.Thread(target=waiter, args=(e,))
t2 = threading.Thread(target=setter, args=(e,))

t1.start()
t2.start()

# 等待两个线程完成
t1.join()
t2.join()

print("主线程:全部完成!")

让我们分解一下:

  1. 我们导入了 threading 模块来创建线程,导入了 time 模块来添加延迟。
  2. 我们定义了两个函数:waitersetter
  3. waiter 函数使用 event.wait() 等待事件被设置。
  4. setter 函数等待3秒钟(模拟一些工作)然后使用 event.set() 设置事件。
  5. 我们创建了一个事件对象 e
  6. 我们创建了两个线程,每个函数一个,并将事件对象传递给两个线程。
  7. 我们启动了两个线程,然后使用 join() 等待它们完成。

运行这个程序后,你会看到服务员在等待,然后3秒后,设置者设置了事件,服务员继续执行。

条件对象

现在,让我们提高一个层次,看看条件对象。它就像是事件对象的更复杂的表亲。

什么是条件对象?

条件对象允许线程等待某些条件变为真。这就像在聚会开始游戏之前等待特定的客人到达。

如何使用条件对象

下面是使用条件对象的一个例子:

import threading
import time
import random

# 共享资源
items = []
condition = threading.Condition()

def producer():
global items
for i in range(5):
time.sleep(random.random())  # 模拟不同的生产时间
with condition:
items.append(f"项目 {i}")
print(f"生产者添加了项目 {i}")
condition.notify()  # 通知消费者项目可用

def consumer():
global items
while True:
with condition:
while not items:
print("消费者正在等待...")
condition.wait()
item = items.pop(0)
print(f"消费者移除了 {item}")
time.sleep(random.random())  # 模拟不同的消费时间

# 创建并启动线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

# 等待生产者完成
producer_thread.join()

# 停止消费者(它在无限循环中)
consumer_thread.daemon = True

让我们分解一下:

  1. 我们创建了一个共享资源 items 和一个条件对象。
  2. producer 函数向列表中添加项目并通知消费者。
  3. consumer 函数等待项目可用,然后移除并“消费”它们。
  4. 我们使用 with condition: 来自动获取和释放条件的锁。
  5. condition.wait() 释放锁并等待通知。
  6. condition.notify() 唤醒一个等待的线程。

这个例子演示了一个经典的生产者-消费者场景,一个线程生产项目,另一个线程消费它们。

事件对象和条件对象比较

以下是事件对象和条件对象的快速比较:

特性 事件 条件
目的 简单信号 复杂同步
状态 二进制(设置/清除) 可以有多个状态
等待 线程等待事件被设置 线程等待特定条件
通知 所有等待的线程都被通知 可以通知一个或所有等待的线程
用例 简单的“行/不行”场景 生产者-消费者问题,复杂同步

结论

恭喜你!你已经迈出了在 Python 中进行线程间通信的第一步。我们介绍了事件对象进行简单信号,以及条件对象进行更复杂的同步场景。

记住,就像学习任何新语言一样,实践是完美的。尝试使用这些对象编写你自己的程序。也许可以创建一个简单的聊天系统,其中线程代表不同的用户,或者使用事件模拟交通灯系统。

线程间通信一开始可能看起来很复杂,但时间和实践后,你将能够像指挥家指挥乐团一样协调线程。继续编码,继续学习,最重要的是,玩得开心!

Credits: Image by storyset