Python实现互斥锁和条件变量

在并发编程中,多个线程同时访问共享数据时可能会产生竞态条件(race condition),导致程序出现不可预料的结果。为了解决这个问题,Python提供了互斥锁和条件变量这两种同步机制。

互斥锁

互斥锁(mutex)是一种用于保护共享资源的同步原语。在任意时刻,只有一个线程能够持有互斥锁,并且其他线程必须等待该线程释放锁之后才能继续执行。

在Python中,可以使用threading模块中的Lock类来创建互斥锁。下面是一个使用互斥锁的示例代码:

import threading

# 创建互斥锁
lock = threading.Lock()

# 共享资源
counter = 0

# 线程函数
def worker():
    global counter
    with lock:  # 通过with语句获取锁
        for _ in range(100000):
            counter += 1

# 创建多个线程并启动
threads = []
for _ in range(10):
    t = threading.Thread(target=worker)
    threads.append(t)
    t.start()

# 等待所有线程执行完毕
for t in threads:
    t.join()

# 打印最终结果
print("Counter:", counter)

在上面的代码中,我们首先创建了一个互斥锁lock。然后定义了一个共享资源counter,它被多个线程同时访问和修改。在worker函数中,我们使用了with lock语句来获取互斥锁。这样一来,每个线程在执行with lock语句块中的代码之前都会尝试获取锁,如果锁已经被其他线程占用,则会被阻塞等待。

最后,我们创建了多个线程并启动它们,然后使用join方法等待所有线程执行完毕。最终,输出的结果应该是Counter: 1000000。由于互斥锁的存在,共享资源在被多个线程访问时能够保持一致性。

条件变量

条件变量(condition variable)是一种用于线程间通信的同步原语。它提供了一个线程等待某个条件成立的机制,并且能够在条件变量的状态发生变化时通知等待的线程。

在Python中,可以使用threading模块中的Condition类来创建条件变量。下面是一个使用条件变量的示例代码:

import threading

# 创建条件变量
condition = threading.Condition()

# 共享资源
buffer = []
buffer_size = 5

# 生产者线程函数
def producer():
    while True:
        with condition:
            # 如果缓冲区已满,则等待消费者消费
            while len(buffer) == buffer_size:
                condition.wait()
            # 生产一个数据并放入缓冲区
            item = len(buffer) + 1
            buffer.append(item)
            print("Produced:", item)
            condition.notify_all()  # 通知消费者消费

# 消费者线程函数
def consumer():
    while True:
        with condition:
            # 如果缓冲区为空,则等待生产者生产
            while len(buffer) == 0:
                condition.wait()
            # 从缓冲区取出一个数据并消费
            item = buffer.pop(0)
            print("Consumed:", item)
            condition.notify_all()  # 通知生产者生产

# 创建生产者线程并启动
t1 = threading.Thread(target=producer)
t1.start()

# 创建消费者线程并启动
t2 = threading.Thread(target=consumer)
t2.start()

# 等待生产者和消费者线程执行完毕
t1.join()
t2.join()

在上面的代码中,我们首先创建了一个条件变量condition。然后定义了一个共享资源buffer,它被生产者线程往里面放入数据,消费者线程从里面取出数据。缓冲区的最大大小由buffer_size