操作系统
进程同步与互斥

实现进程互斥的软件办法

互斥锁
又名自旋锁,适用于多处理器。进入临界区的过程中,如果拿不到临界资源,需要消耗完时间片,才会让出CPU,违反“让权等待”。
信号量

一种完美的方案: 记录型信号量,可以解决“让权等待”。其数据结构如下:
class semaphore:
def __init__(self, value=1):
self.value = value # 当前临界区资源数量
self.queue = Queue() # 当前资源对应的等待队列
def wait(self):
self.value -= 1
if self.value < 0:
process = GET_CURRENT_PROCESS() # 获取当前运行的进程,阻塞
BLOCK(process)
self.queue.put(process) # 加入到等待队列
def release(self):
self.value += 1
if self.value >= 0 and self.queue:
process = self.queue.pop() # 恢复等待队列的第一个进程
WAKEUP(process)
无法实现“让权等待”的原因是:当不能获取临界资源时,无法主动让出CPU资源,需要等时间片走完,基于时间中断机制让出CPU。记录型信号量可以主动阻塞无法进入临界资源的进程,将其加入队列,促使其让出CPU。

以下信号量,皆代表记录型信号量。
基于信号量实现互斥与同步

# semaphore_mutex.py
# author: shayuewt
# email: wt0504@zju.edu.cn
"""本代码是信号量模拟互斥的示例代码"""
import time
import threading
# 初始化一个信号量,模拟临界资源的访问
mutex = threading.Semaphore(1)
def mock_mutex(task_id):
# 如果信号量的值大于0,则获取并减1,否则阻塞等待
# mutex.acquire()
print(f"Worker {task_id} 开始执行任务...")
# 这里代表进行某种可能需要互斥访问的资源操作
# 模拟耗时操作
time.sleep(2)
print(f"Worker {task_id} 完成任务.")
# mutex.release()
def main():
# 创建2个线程来模拟同时请求资源
for i in range(2):
t = threading.Thread(target=mock_mutex, args=(i,))
t.start()
# 确保所有线程都执行完毕
for t in threading.enumerate():
if t is not threading.currentThread():
t.join()
print("所有工作完成")
main()
"""
若加信号量,执行顺序:
Worker 0 开始执行任务...
Worker 0 完成任务.
Worker 1 开始执行任务...
Worker 1 完成任务.
所有工作完成
若不加信号量,可能的执行顺序:
Worker 0 开始执行任务...
Worker 1 开始执行任务...
Worker 1 完成任务.
Worker 0 完成任务.
所有工作完成
"""
# semaphore_sync.py
# author: shayuewt
# email: wt0504@zju.edu.cn
"""本代码是信号量模拟同步的示例代码
共计5个worker,要求按照先后顺序如下执行:
worker0 -> worker1 -> worker2 -> worker4
->->-> worker3 ->->->
"""
import time
import threading
# 初始化一个信号量,模拟同步操作的访问
syncs0_1 = threading.Semaphore(0)
syncs1_2 = threading.Semaphore(0)
syncs2_4 = threading.Semaphore(0)
syncs0_3 = threading.Semaphore(0)
syncs3_4 = threading.Semaphore(0)
def worker0():
print("在 worker0 中打印结束...")
time.sleep(1)
syncs0_1.release()
syncs0_3.release()
def worker1():
syncs0_1.acquire()
print("在 worker1 中打印结束...")
time.sleep(1)
syncs1_2.release()
def worker2():
syncs1_2.acquire()
print("在 worker2 中打印结束...")
time.sleep(1)
syncs2_4.release()
def worker3():
syncs0_3.acquire()
print("在 worker3 中打印结束...")
time.sleep(1)
syncs3_4.release()
def worker4():
syncs2_4.acquire()
syncs3_4.acquire()
print("在 worker4 中打印结束...")
time.sleep(1)
def main():
# 创建5个线程来模拟同时请求资源
t4 = threading.Thread(target=worker4, args=())
t4.start()
t3 = threading.Thread(target=worker3, args=())
t3.start()
t2 = threading.Thread(target=worker2, args=())
t2.start()
t1 = threading.Thread(target=worker1, args=())
t1.start()
t0 = threading.Thread(target=worker0, args=())
t0.start()
# 确保所有线程都执行完毕
for t in threading.enumerate():
if t is not threading.currentThread():
t.join()
print("所有工作完成")
main()
"""可以看到输出执行顺序如下:
在 worker0 中打印结束...
在 worker1 中打印结束...
在 worker3 中打印结束...
在 worker2 中打印结束...
在 worker4 中打印结束...
所有工作完成
虽然按照时间先后顺序,worker4 最先进入调用,但它被阻塞到最后才打印
"""
生产者-消费者问题

# producer_consumer.py
# author: shayuewt
# email: wt0504@zju.edu.cn
"""本代码是信号量解决生产者消费者的示例代码"""
import time
import random
import threading
# 初始化一个临界资源,假定不超过5
buffer = []
# 初始化一个互斥信号量,模拟对临界资源的访问
mutex = threading.Semaphore(1)
# 初始化两个同步信号量,模拟生产者、消费者之间的同步关系
# ... -> 消费者 -> 生产者 -> 消费者 -> 生产者 -> ...
empty = threading.Semaphore(5) # 表示空闲缓冲区的数量,如果缓冲区没有空闲,生产者需要被 block
full = threading.Semaphore(0) # 表达非空缓冲区的数量,如果缓冲区没有数据,消费者需要被 block
def consumer():
"""对临界资源中的数据进行消费"""
full.acquire() # 如果没有资源可消费,那么就会被 block
# 临界区执行
mutex.acquire()
print(f"开始消费...此时 buffer: {buffer}")
buffer.pop(0)
print(f"消费完毕...此时 buffer: {buffer}")
mutex.release()
empty.release()
def producer():
"""对临界资源中的数据进行生产"""
empty.acquire() # 如果没有资源可生产,那么就会被 block
# 临界区执行
mutex.acquire()
print(f"开始生产...此时 buffer: {buffer}")
buffer.append(random.randint(20, 100))
print(f"生产完毕...此时 buffer: {buffer}")
mutex.release()
full.release()
def main():
threads = []
for _ in range(20):
funcs = [consumer, producer] if random.random() < 0.5 else [producer, consumer]
c0 = threading.Thread(target=funcs[0], args=())
c0.start()
if random.random() < 0.1:
time.sleep(1)
c1 = threading.Thread(target=funcs[1], args=())
c1.start()
if random.random() < 0.1:
time.sleep(1)
threads.extend([c0, c1])
# 确保所有线程都执行完毕,否则阻塞主程序
for t in threads:
if t is not threading.currentThread():
t.join()
print("所有工作完成")
main()





浙公网安备 33010602011771号