操作系统
进程同步与互斥

实现进程互斥的软件办法

互斥锁
又名自旋锁,适用于多处理器。进入临界区的过程中,如果拿不到临界资源,需要消耗完时间片,才会让出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号