十九、信号量和管程

简介: 十九、信号量和管程

1、信号量-semaphore


1.1 抽象数据类型

一个整型(sem),两个原子操作;


P():sem减1,如果  sem<0,等待,否则继续;


V():sem加1,如果  sem≤0,唤醒一个等待的进程




1.2 信号量的属性


信号量是整数;


信号量是被保护的变量,初始化完成之后,唯一改变一个信号量的值的方法是通过P()和V()操作,操作对象必须是原子;


P()能够阻塞,V()不会阻塞;



1.3 两种类型的信号量


二进制信号量:可以是0或者是1,将semaphore的初始值设置为1,可以模拟lock机制;


一般/计数信号量:可以取任何非负值,将semaphore的初始值设置为一个正数( n n n)这种情况下可以允许 n n n个进程进入到临界区进行执行,不同于lock机制下只允许一个进程进入临界区进行执行;


信号量可以用于互斥和条件同步2个方面。




1.4 使用信号量实现消费者问题


消费者问题表述为:一个或者多个生产者向buffer中填充数据;单个消费者每次从缓冲区中取出数据;在任意时间内只有一个生产者或者消费者可以访问缓冲区;


439f1c747d264a438a0660f5a844ffcd.png

其正确性要求:在一个时间内只能有一个线程操作缓冲区(互斥);当缓冲区为空时,消费者必须等待生产者向缓冲区填充数据(同步);当缓冲区填满之后,生产者必须等待消费者从缓冲区取出数据之后才能继续填充数据(同步)。上述正确性要求可以通过三个信号量来处理三个不同的约束:一个二进制信号量处理互斥;一个一般信号量处理第一个同步;一个一般信号量处理第二个同步。


最终实现逻辑如下图所示:

0a325d5a60764f818e560387a0524fd4.png

上述逻辑中,V()操作的顺序可以互换,P()操作的顺序不能互换,会导致死锁。




1.5 信号量的底层实现

class Semaphore
{//信号量结构体
  int sem;    //计数变量
  waitQueue q;  //等待队列
}


Semaphore::P()
{//信号量的P操作实现
  --sem;
  if(sem<0)
  {
    Add this thread t to q;
    block(p);//使进程p进行睡眠
  }
}
Semaphore::V()
{//信号量的V操作实现 
  ++sem;
  if(sem<=0)
  {
    Remove a thread t from q;
    wakeup(t);//唤醒进程t,使用FIFO的顺序
  }
}




2、管程


目的: 分离互斥和条件同步的关注;概念: 管程中包含一个锁:用来指定临界区;管程中还包含0或者多个条件变量:用来等待/通知信号量用于管理并发访问的共享数据。根据条件的个数来设定条件变量的数量。线程进入管程时,同一时间只有一个线程能够进入。管程的大致结构如下图所示:

2cd16a3eceb446b58417000f2460e7a7.png


2.1 条件变量

条件变量 允许等待状态进入临界区,允许处于等待状态的线程进入临界区,某个时刻院子释放锁进入睡眠。条件变量包含以下两个操作:wait() 释放锁,睡眠,重新获得锁(返回后);signal() 唤醒等待者,如果有的话。条件变量的实现如下所示:

Class Condition
{//条件变量的结构体
  int numWaiting = 0;
  WaitQueue q;
}
Condition::Wait(lock)
{//条件变量的wait操作
  ++numWaiting;     //表示挂在当前条件变量上睡眠的线程多了一个
  Add this thread t to q;//把线程加到等待队列里面
  release(lock);      //释放锁
  shedule();//need mutex  //选择下一个线程去执行
  require(lock);      //获得锁
}


Condition::Signal()
{//条件变量的signal操作,其实wait操作的反向操作
  if(numWaiting>0)//判断是否有挂在当前条件变量上的睡眠的线程
  {
    Remove a thread t from q;//将一个睡眠线程从等待队列中取出来,FIFO规则
    wakeup(t);//need mutex   //唤醒当前的线程t
    --numWaiting;      //挂在条件变量上的睡眠的线程数量减1
  }
}



2.2 使用管程解决生产消费问题


使用管程解决生产者消费者问题的过程如下,左边为生产者,右边为消费者。

9aede591b53e40119353c287af64dde7.png

相关文章
|
6月前
|
存储 缓存 Linux
Linux多线程【初识线程】
Linux多线程【初识线程】
45 0
|
Java 调度 开发者
【JavaSE专栏84】线程让步,一种线程调度的机制
【JavaSE专栏84】线程让步,一种线程调度的机制
107 0
|
1月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
33 1
|
1月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
4月前
|
消息中间件 算法 Java
(十四)深入并发之线程、进程、纤程、协程、管程与死锁、活锁、锁饥饿详解
本文深入探讨了并发编程的关键概念和技术挑战。首先介绍了进程、线程、纤程、协程、管程等概念,强调了这些概念是如何随多核时代的到来而演变的,以满足高性能计算的需求。随后,文章详细解释了死锁、活锁与锁饥饿等问题,通过生动的例子帮助理解这些现象,并提供了预防和解决这些问题的方法。最后,通过一个具体的死锁示例代码展示了如何在实践中遇到并发问题,并提供了几种常用的工具和技术来诊断和解决这些问题。本文旨在为并发编程的实践者提供一个全面的理解框架,帮助他们在开发过程中更好地处理并发问题。
|
6月前
|
存储 缓存 Linux
Linux驱动开发(锁和信号量的概念及实现原理)
Linux驱动开发(锁和信号量的概念及实现原理)
121 0
|
6月前
|
安全 Linux C语言
Linux系统编程(线程同步 互斥锁)
Linux系统编程(线程同步 互斥锁)
71 0
|
Linux
一文读懂Linux多线程中互斥锁、读写锁、自旋锁、条件变量、信号量
Hello、Hello大家好,我是木荣,今天我们继续来聊一聊Linux中多线程编程中的重要知识点,详细谈谈多线程中同步和互斥机制。
8580 1
一文读懂Linux多线程中互斥锁、读写锁、自旋锁、条件变量、信号量
|
Linux
Linux驱动开发——并发和竞态(信号量方式的使用④)
Linux驱动开发——并发和竞态(信号量方式的使用④)
162 0
Linux驱动开发——并发和竞态(信号量方式的使用④)