Python - 线程同步

大家好,未来的Python巫师们!今天,我们将开始一段令人激动的旅程,进入线程同步的世界。想象一下,你正在指挥一个乐队,每个乐手都是一个线程,你需要确保他们都能和谐地演奏。在编程中,线程同步本质上就是如此!

Python - Synchronizing Threads

使用锁进行线程同步

让我们从同步工具箱中最基本的工具开始:锁。将锁想象成酒店房间门上的“请勿打扰”标志。当一个线程获得锁时,就像挂上了这个标志,告诉其他线程:“嘿,我在这里很忙!”

以下是一个简单的示例来说明这个概念:

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