常用进程同步方法对共享数据段访问的协调
- 信号量(Semaphore):
- 信号量是一个整型变量,它通过一个计数器来控制对共享资源的访问。
- 当进程想要访问共享数据段时,它首先尝试获取信号量(将计数器减1)。如果计数器的值大于0,获取成功,进程可以访问共享数据;如果计数器的值为0,进程会被阻塞,直到其他进程释放信号量(将计数器加1)。
- 例如,假设有一个共享缓冲区,信号量初始值设为缓冲区的容量。生产者进程每次向缓冲区写入数据前获取信号量,如果信号量计数器大于0,意味着缓冲区有空间,生产者可以写入,然后释放信号量;消费者进程每次从缓冲区读取数据前也获取信号量,读取后释放信号量。这样就保证了对共享缓冲区这一共享数据段的有序访问。
- 互斥锁(Mutex):
- 互斥锁本质上是一种特殊的二元信号量(值为0或1)。
- 当进程想要访问共享数据段时,它尝试获取互斥锁。如果互斥锁的值为1(表示未被占用),获取成功,进程可以访问共享数据,并将互斥锁的值设为0(表示已被占用);当进程访问完共享数据后,释放互斥锁,将其值设为1。
- 若互斥锁的值为0(已被其他进程占用),当前进程会被阻塞,直到持有互斥锁的进程释放它。这就确保了在同一时刻只有一个进程能够访问共享数据段,避免了数据竞争。
生产者 - 消费者模型示例
- 使用Python和多进程模块以及信号量实现:
import multiprocessing
import time
# 缓冲区大小
BUFFER_SIZE = 5
# 信号量,初始值为缓冲区大小
empty = multiprocessing.Semaphore(BUFFER_SIZE)
# 信号量,初始值为0
full = multiprocessing.Semaphore(0)
# 互斥锁
mutex = multiprocessing.Lock()
# 共享缓冲区
buffer = multiprocessing.Array('i', [0] * BUFFER_SIZE)
in_index = 0
out_index = 0
def producer(id):
global in_index
while True:
empty.acquire() # 获取空缓冲区信号量
mutex.acquire() # 加锁
item = id * 10 + in_index
buffer[in_index] = item
print(f"Producer {id} produced {item}")
in_index = (in_index + 1) % BUFFER_SIZE
mutex.release() # 解锁
full.release() # 释放满缓冲区信号量
time.sleep(1)
def consumer(id):
global out_index
while True:
full.acquire() # 获取满缓冲区信号量
mutex.acquire() # 加锁
item = buffer[out_index]
print(f"Consumer {id} consumed {item}")
out_index = (out_index + 1) % BUFFER_SIZE
mutex.release() # 解锁
empty.release() # 释放空缓冲区信号量
time.sleep(1)
if __name__ == '__main__':
producer1 = multiprocessing.Process(target=producer, args=(1,))
consumer1 = multiprocessing.Process(target=consumer, args=(1,))
producer1.start()
consumer1.start()
producer1.join()
consumer1.join()
- 代码解释:
empty
信号量控制缓冲区的空闲空间,初始值为BUFFER_SIZE
。
full
信号量控制缓冲区中的数据,初始值为0。
mutex
互斥锁保证对共享缓冲区buffer
的互斥访问。
producer
函数模拟生产者,每次生产一个数据前先获取empty
信号量,获取成功后加锁,将数据放入缓冲区,解锁并释放full
信号量。
consumer
函数模拟消费者,每次消费数据前先获取full
信号量,获取成功后加锁,从缓冲区取出数据,解锁并释放empty
信号量。