Python多线程操作之互斥锁、递归锁、信号量、事

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函数就可以继续执行。

后端开发标签