信号量与进程/线程间同步与互斥

术语

  • 进度图 进度图是将 n 个并发进程的执行模型化为一条 n 维笛卡尔空间中的轨迹线,每条轴 k 对应于线程 k 的进度,每个进度对应一条指令。
  • 临界区 某个线程操作共享变量 cnt 内容的一串指令
  • 不安全区 两个临界区的交集形成的状态空间区域称为不安全区
  • 安全轨迹区 绕开不安全区的轨迹线
  • 信号量 是一个具有非负整数值的全局变量,只能由两种特殊的操作 P(s)(测试减一) 和 V(s)(增加加一) 来处理,这两个操作都是原子的,不可分割的
    信号量与进程/线程间同步与互斥_第1张图片
    临界区与安全轨迹线

    为了保证线程化程序的正确执行(实际上是任何共享全局数据结构的冰法程序的正确执行)我们必须以某种方式同步线程,使它们总是有一条安全轨迹线,一个经典的方法是基于信号量的思想。

使用信号量来实现互斥

基本思想是将每个共享变量与一个信号量 s(初始化为一个整数 n) 联系起来,然后用 P(s) 和 V(s) 操作将相应的临界区包围起来。
s 的初始值决定了这个资源可以同时被 n 个进程使用
n=1 时的信号量成为互斥锁(mutex),P(s) 和 V(s)相应的成为加锁和解锁,信号量操作确保了对临界区的互斥访问。

信号量与进程/线程间同步与互斥_第2张图片
使用信号量实现互斥

使用信号量来调度共享资源

除了提供互斥之外,信号量的另外一个重要作用是用来调度对共享资源的访问,即一个线程用信号量来通知另一个线程,线程状态中的某个条件已经为真了。

生产者-消费者问题

生产者消费者问题也称为有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后。
经典进程同步问题1:生产者-消费者问题

semaphore mutex=1; //临界区互斥信号量
semaphore empty=n;  //空闲缓冲区
semaphore full=0;  //缓冲区初始化为空
producer () { //生产者进程
   while(1){
       produce an item in nextp;  //生产数据
       P(empty);  //获取空缓冲区单元
       P(mutex);  //进入临界区.
       add nextp to buffer;  //将数据放入缓冲区
       V(mutex);  //离开临界区,释放互斥信号量
       V(full);  //满缓冲区数加1
   }
}
consumer () {  //消费者进程
   while(1){
       P(full);  //获取满缓冲区单元
       P(mutex);  // 进入临界区
       remove an item from buffer;  //从缓冲区中取出数据
       V (mutex);  //离开临界区,释放互斥信号量
       V (empty) ;  //空缓冲区数加1
       consume the item;  //消费数据
   }
}

读者写者问题

有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。
经典进程同步问题2:读者-写者问题

int count = 0;  //用于记录当前的读者数量
semaphore mutex = 1;  //用于保护更新count变量时的互斥
semaphore rw=1;  //用于保证读者和写者互斥地访问文件
semaphore w=1;  //用于实现“写优先”
writer(){
    while(1){
        P(w);  //在无写进程请求时进入
        P(rw);  //互斥访问共享文件
        writing;  //写入
        V(rw);  // 释放共享文件
        V(w) ;  //恢复对共享支件的访问
    }
}
reader () {  //读者进程
    while (1){
        P (w) ;  // 在无写进程请求时进入
        P (mutex);  // 互斥访问count变量
        if (count==0)  //当第一个读进程读共享文件时
            P(rw);  //阻止写进程写
        count++;  //读者计数器加1
        V (mutex) ;  //释放互斥变量count
        V(w);  //恢复对共享文件的访问
        reading;  //读取
        P (mutex) ; //互斥访问count变量
        count--;  //读者计数器减1
        if (count==0)  //当最后一个读进程读完共享文件
            V(rw);  //允许写进程写
        V (mutex);  //释放互斥变量count
    }
}

你可能感兴趣的:(信号量与进程/线程间同步与互斥)