Python - 线程同步
大家好,未来的Python巫师们!今天,我们将开始一段令人激动的旅程,进入线程同步的世界。想象一下,你正在指挥一个乐队,每个乐手都是一个线程,你需要确保他们都能和谐地演奏。在编程中,线程同步本质上就是如此!
使用锁进行线程同步
让我们从同步工具箱中最基本的工具开始:锁。将锁想象成酒店房间门上的“请勿打扰”标志。当一个线程获得锁时,就像挂上了这个标志,告诉其他线程:“嘿,我在这里很忙!”
以下是一个简单的示例来说明这个概念:
import threading
import time
# 共享资源
counter = 0
# 创建一个锁
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
lock.acquire()
counter += 1
lock.release()
# 创建两个线程
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
# 启动线程
thread1.start()
thread2.start()
# 等待线程完成
thread1.join()
thread2.join()
print(f"最终的计数器值:{counter}")
在这个例子中,我们有一个共享资源counter
,两个线程试图增加它的值。如果没有锁,我们可能会遇到竞争条件,两个线程同时尝试增加计数器,可能会导致不正确的结果。
通过在修改计数器之前使用lock.acquire()
,并在之后使用lock.release()
,我们确保一次只有一个线程可以增加计数器。这就像在接力赛中传递接力棒——只有持有接力棒(锁)的线程才能运行(修改共享资源)。
使用条件对象同步Python线程
现在,让我们用条件对象提升我们的同步游戏。这些条件对象就像我们线程的复杂交通信号灯,允许更复杂的协调。
以下是一个使用条件对象的生产者-消费者场景的例子:
import threading
import time
import random
# 共享缓冲区
buffer = []
MAX_SIZE = 5
# 创建一个条件对象
condition = threading.Condition()
def producer():
global buffer
while True:
with condition:
while len(buffer) == MAX_SIZE:
print("缓冲区已满,生产者在等待...")
condition.wait()
item = random.randint(1, 100)
buffer.append(item)
print(f"生产了:{item}")
condition.notify()
time.sleep(random.random())
def consumer():
global buffer
while True:
with condition:
while len(buffer) == 0:
print("缓冲区为空,消费者在等待...")
condition.wait()
item = buffer.pop(0)
print(f"消费了:{item}")
condition.notify()
time.sleep(random.random())
# 创建生产者和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
# 启动线程
producer_thread.start()
consumer_thread.start()
# 运行一段时间
time.sleep(10)
在这个例子中,我们有一个生产者向缓冲区添加项目,一个消费者从缓冲区移除项目。条件对象帮助协调他们的动作:
- 生产者在缓冲区满时等待。
- 消费者在缓冲区为空时等待。
- 他们互相通知何时可以继续。
这就像一场编排精良的舞蹈,条件对象就是编舞者!
使用join()方法同步线程
join()
方法就像告诉一个线程在另一个线程完成其表演之前等待,然后才能上台。这是一种简单而强大的线程同步方式。
以下是一个例子:
import threading
import time
def worker(name, delay):
print(f"{name} 开始...")
time.sleep(delay)
print(f"{name} 完成!")
# 创建线程
thread1 = threading.Thread(target=worker, args=("线程 1", 2))
thread2 = threading.Thread(target=worker, args=("线程 2", 4))
# 启动线程
thread1.start()
thread2.start()
# 等待线程1完成
thread1.join()
print("主线程在线程1之后等待")
# 等待线程2完成
thread2.join()
print("主线程在线程2之后等待")
print("所有线程已完成!")
在这个例子中,主线程启动了两个工作线程,然后使用join()
等待每个线程完成。这就像一个父母在服务晚餐之前等待他们的孩子完成作业!
其他同步原语
Python提供了几种其他用于线程同步的工具。让我们快速看一下其中的一些:
原语 | 描述 | 使用场景 |
---|---|---|
信号量 | 允许有限数量的线程访问资源 | 管理数据库连接池 |
事件 | 允许一个线程向其他线程发出事件信号 | 表示任务已完成 |
屏障 | 允许多个线程等待直到所有达到某个点 | 同步比赛的开始 |
以下是一个使用信号量的快速示例:
import threading
import time
# 创建一个允许两个线程同时访问的信号量
semaphore = threading.Semaphore(2)
def worker(name):
with semaphore:
print(f"{name} 获得了信号量")
time.sleep(1)
print(f"{name} 释放了信号量")
# 创建并启动5个线程
threads = []
for i in range(5):
thread = threading.Thread(target=worker, args=(f"线程 {i}",))
threads.append(thread)
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
print("所有线程已完成!")
在这个例子中,信号量就像一个俱乐部的保镖,一次只允许两个线程进入。这对于需要限制对稀缺资源访问的情况非常完美!
就这样,各位!我们已经探索了Python中的线程同步的精彩世界。请记住,就像指挥乐队或编排舞蹈一样,同步线程都是关于协调和时机的。有了这些工具,您就可以创建和谐的多线程Python程序了。继续练习,保持好奇心,祝编程愉快!
Credits: Image by storyset