1. 互斥锁(Mutex)
在多线程编程中,由于多个线程可能同时访问某个共享资源,就可能会出现多线程竞争的情况。为了保证数据的正确性,就需要使用锁来进行同步控制。
互斥锁(Mutex)是一种最基本的锁,同一时刻只允许一个线程访问共享资源,其他线程需要等待锁的释放才能访问。
1.1 使用互斥锁
Python内置的线程模块中提供了Lock类来实现互斥锁,该类有两个主要的方法:
acquire([blocking]):获取锁。默认情况下,如果该锁已经被其他线程获取,则当前线程会被阻塞,直到锁被释放。如果传递了blocking参数为False,则获取不到锁时会直接返回False,而不是等待锁的释放。
release():释放锁。
下面是一个使用互斥锁的示例:
import threading
# 定义共享变量和锁
count = 0
lock = threading.Lock()
# 自增函数,使用互斥锁保证安全
def increment():
global count
lock.acquire()
count += 1
lock.release()
# 创建多个线程并启动
threads = []
for i in range(100):
threads.append(threading.Thread(target=increment))
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(count) # 输出结果为100
在上面的示例中,我们定义了一个全局变量count和一个互斥锁lock。在increment函数中,我们先获取锁,然后执行自增操作,最后再释放锁。这样就可以保证多个线程访问count变量时不会出现竞争。
1.2 互斥锁的问题
互斥锁虽然能够保证线程安全,但是也存在一些问题:
锁的使用在并发量高的情况下会大大降低程序的性能。
当代码中存在多个互斥锁时,容易出现死锁的情况。
2. 递归锁(RLock)
递归锁(RLock)是一种特殊的互斥锁,与互斥锁的不同之处在于,当同一线程多次请求同一把锁时,不会造成死锁。
2.1 使用递归锁
Python线程模块中提供了RLock类用于实现递归锁。示例代码如下:
import threading
# 定义递归锁和计数器变量
rlock = threading.RLock()
count = 0
# 循环递归函数
def recursive():
global count
rlock.acquire() # 获取递归锁
count += 1
if count < 5:
recursive()
rlock.release() # 释放递归锁
# 创建多个线程并启动
threads = []
for i in range(3):
threads.append(threading.Thread(target=recursive))
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(count) # 输出结果为15
在上面的示例中,我们使用RLock定义了一个递归锁,并通过递归函数来测试递归锁的效果。在递归函数中,我们先获取递归锁,执行自增操作,然后再递归调用自己。由于我们使用了递归锁,所以即使在递归调用时也不会陷入死锁。
3. 信号量(Semaphore)
信号量(Semaphore)是一种允许多个线程同时访问一个共享资源的锁。
3.1 使用信号量
Python线程模块中提供了Semaphore类用于实现信号量。Semaphore类有两个主要的方法:
acquire([blocking]):获取信号量。如果信号量计数器为0,则该调用被阻塞,直到有线程调用release()方法释放信号量。
release():释放信号量,增加信号量计数器的值。
下面是一个使用信号量的示例:
import threading
# 定义信号量和共享变量
semaphore = threading.Semaphore(2) # 最多允许2个线程同时执行
count = 0
# 自增函数
def increment():
global count
with semaphore: # 获取信号量
count += 1
# 创建多个线程并启动
threads = []
for i in range(5):
threads.append(threading.Thread(target=increment))
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(count) # 输出结果为5
在上面的示例中,我们定义了一个Semaphore对象,最多允许2个线程同时执行。在increment函数中,我们使用with语句获取信号量,并执行自增操作。由于信号量的限制,最多只有2个线程可以同时执行increment函数。
4. 事件(Event)
事件(Event)是一种用于线程间通信的机制。当一个线程需要等待另一个线程的状态变化时,可以使用事件来进行同步。
4.1 使用事件
Python线程模块中提供了Event类来实现事件。Event类有三个主要的方法:
wait():等待事件的状态变为True。
set():将事件的状态设置为True。
clear():将事件的状态设置为False。
下面是一个使用事件的示例:
import threading
import time
# 定义事件和共享变量
event = threading.Event()
flag = False
# 等待事件函数
def wait_for_event():
print('Waiting for event...')
event.wait() # 等待事件
print('Event is set, do something...')
time.sleep(1)
print('Finish doing something...')
# 触发事件函数
def trigger_event():
global flag
time.sleep(2)
flag = True
event.set() # 设置事件为True
# 创建等待线程和触发线程并启动
thread1 = threading.Thread(target=wait_for_event)
thread1.start()
thread2 = threading.Thread(target=trigger_event)
thread2.start()
thread1.join()
thread2.join()
在上面的示例中,我们定义了一个事件对象和一个共享变量flag。在wait_for_event函数中,我们调用了event.wait()方法等待事件,直到事件的状态变为True。在trigger_event函数中,我们先等待2秒,然后将flag设置为True,并调用event.set()方法触发事件。这样wait_for_event函数就可以继续执行。