• 信号量和管程


    信号量和管程 \color{orange}{\huge{信号量和管程}} 信号量和管程

    信号量 \purple{信号量} 信号量

    抽象数据类型 抽象数据类型 抽象数据类型

    1. 一个整形,附加着两个原子操作。
    2. P P P操作: s e m − 1 sem-1 sem1,如果 s e m < 0 sem < 0 sem<0,则进入线程睡眠,开始等待,否则继续。
    3. V V V操作:如果 s e m < = 0 sem <= 0 sem<=0,唤醒一个正在睡眠等待的 P P P

    便于理解的解释: \color{blue}{便于理解的解释:} 便于理解的解释:
    1.最开始的时候两条铁轨上,没有没有火车,铁轨数量相当于信号量,当一辆火车进入铁轨的时候,执行 P P P操作,这个时候铁轨数量 − 1 -1 1( 信号量数量 − 1 \red{信号量数量-1} 信号量数量1)。在这里插入图片描述
    2. 当所有的铁轨都被占满的时候( 信号量数量 < = 0 \red{信号量数量 <= 0} 信号量数量<=0),如果又来了火车,此时执行 P P P操作会因为 信号量数量 < = 0 信号量数量 <= 0 信号量数量<=0而进入等待状态。
    在这里插入图片描述
    3. 当铁轨中有的火车运行完毕之后,从分叉铁轨出来的时候,执行 V V V操作,可使用铁轨数量 + 1 +1 +1( 信号量数量 + 1 \red{信号量数量 + 1} 信号量数量+1),并且向一个正在等待的火车传递信息( 睡眠线程的唤醒 \red{睡眠线程的唤醒} 睡眠线程的唤醒)。然后该等待的火车进入铁轨。
    在这里插入图片描述在这里插入图片描述

    信号量的使用 \purple{信号量的使用} 信号量的使用

    ①. 信号量特征: 信号量特征: 信号量特征:

    1. 信号量是 正数 \red{正数} 正数
    2. 信号量是一个 被保护 \red{被保护} 被保护的变量。
      被保护 \color{olive}{被保护} 被保护:信号量只要初始化了之后, 只能通过 P 与 V 两个操作进行改变 ∣ \red{只能通过P与V两个操作进行改变}| 只能通过PV两个操作进行改变
    3. P P P操作会阻塞,而 V V V操作不会。

    ②. 信号量种类: 信号量种类: 信号量种类:

    1. 二进制信号量 \red{二进制信号量} 二进制信号量 0 0 0 1 1 1( L o c k \blue{Lock} Lock)
    2. 一般信号量 \red{一般信号量} 一般信号量:可以取得任何值( 多线程适用 \blue{多线程适用} 多线程适用)

    ③. 信号量作用: 信号量作用: 信号量作用:

    1. 线程互斥
    2. 条件同步

    有界缓冲区的生产者——消费者问题 \purple{有界缓冲区的生产者——消费者问题} 有界缓冲区的生产者——消费者问题

    在这里插入图片描述

    正确性要求: \red{正确性要求:} 正确性要求:
    1. 互斥 \blue{互斥} 互斥:同一时间只能够有一个线程操作缓冲区。
    2. 调度 / 同步约束 \blue{调度/同步约束} 调度/同步约束
      缓冲区空,那么消费者必须等待生产者。
      缓冲区满,那么生产者必须等待消费者。
    信号量 : \red{信号量:} 信号量:
    1. 二进制信号量(互斥)
    2. 计数信号量 F u l l B u f f e r s FullBuffers FullBuffers E m p t y B u f f e r s EmptyBuffers EmptyBuffers
    伪代码描述: \red{伪代码描述:} 伪代码描述:
    Class BoundedBuffer{				//缓冲区类型
    	mutex = new Semaphore(1);			//互斥信号量
    	FullBuffers = new Semaphore(0);		//缓冲区满信号量
    	EmptyBuffers = new Semaphore(n);	//缓冲区空信号量
    }
    
    BoundedBuffer::Deposit(e) {			//生产者的Deposit存储资源操作
    	EmptyBuffers -> P();
    	mutex -> P();
    	Add c to the buffer;
    	mutex -> V();
    	FullBuffers -> V();
    }
    
    BoundedBuffer::Remove(e) {			//消费者的Remove取用资源操作
    	FullBuffers -> P();
    	mutex -> P();
    	Remove c from buffer;
    	mutex -> V();
    	EmptyBuffers -> V();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    整体流程: \blue{整体流程:} 整体流程:
    D e p o s i t : \purple{\huge{Deposit:}} Deposit
    生产者首先对于 E m p t y B u f f e r s EmptyBuffers EmptyBuffers信号量执行 P P P操作希望能够获得向缓冲区存储数据的权利( E m p t y B u f f e r s 信号量的值代表缓冲区同时可以进入多少个生产者 \red{EmptyBuffers信号量的值代表缓冲区同时可以进入多少个生产者} EmptyBuffers信号量的值代表缓冲区同时可以进入多少个生产者),之后对于 m u t e x mutex mutex信号量执行 P P P操作开启线程互斥( m u t e x = 1 → m u t e x = 0 mutex = 1 →mutex = 0 mutex=1mutex=0),意为当前临界区已经被使用了,拒绝其他线程的访问。存储完数据之后,首先使用 m u t e x mutex mutex V V V操作打开临界区的访问权限( m u t e x = 1 → m u t e x = 0 mutex = 1 →mutex = 0 mutex=1mutex=0),之后调用 F u l l B u f f e r s FullBuffers FullBuffers V V V操作告知消费者现在缓冲区里面的已经有了数据了( F u l l B u f f e r s 信号量的值代表缓冲区中存储的数据个数 \red{FullBuffers信号量的值代表缓冲区中存储的数据个数} FullBuffers信号量的值代表缓冲区中存储的数据个数)。
    R e m o v e : \purple{\huge{Remove:}} Remove
    消费者首先对于 F u l l B u f f e r s FullBuffers FullBuffers执行 P P P操作,意为从缓冲区中读出数据( F u l l B u f f e r s 信号量初始化的值是 0 ,如果一开始没有数据,那么消费者会会被直接挡在这一步,等待消费者添加数据 \red{FullBuffers信号量初始化的值是0,如果一开始没有数据,那么消费者会会被直接挡在这一步,等待消费者添加数据} FullBuffers信号量初始化的值是0,如果一开始没有数据,那么消费者会会被直接挡在这一步,等待消费者添加数据)。之后进行线程互斥操作,对 m u t e x mutex mutex信号量执行 P P P操作,然后取出数据。之后放开对于临界区的访问管控( m u t e x mutex mutex = 0 → m u t e x mutex mutex = 1)。最后对于 E m p t y B u f f e r s EmptyBuffers EmptyBuffers信号量执行 V V V操作,这时可以将睡眠的生产者进程唤醒。

    信号量基本操作的实现 \purple{信号量基本操作的实现} 信号量基本操作的实现( P P P V V V操作)

    class Semaphore{
    	int sem;
    	WaitQueue q;
    }
    
    Semaphore::P(){			//P操作实现基本原理
    	sem--;
    	if(sem < 0){
    		Add this thread to q:
    		block(p);
    	}
    }
    
    Semaphore::V(){			//V操作实现基本原理
    	sem++;
    	if(sem <= 0){
    		Remove a thread t from q;
    		wakeup(t);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    S e m + + 和 S e m − − 的理解: \color{red}{Sem++和Sem--的理解:} Sem++Sem的理解:可以理解为,当 s e m < 0 sem < 0 sem<0的时候开始,对线程进行入队(等待队列)。 S e m − − Sem-- Sem就是对线程进行入队, S e m + + Sem++ Sem++就是对等待队列中的等待线程进行出队,然后调用 W a k e u p ( 出队线程 ) Wakeup(出队线程) Wakeup(出队线程)函数进行唤醒。

    管程 ( M o n i t o r ) \purple{管程(Monitor)} 管程(Monitor)

    管程定义 \red{管程定义} 管程定义

    包含了一系列的共享变量以及操作这些共享变量的函数的集合。

    管程构成: \red{管程构成:} 管程构成:

    1. L o c k : Lock: Lock指定临界区。
    2. 0 或多个条件变量: 0或多个条件变量: 0或多个条件变量:等待/通知信号量用于管理和并发访问共享数据。

    条件变量 W a i t 和 S i g n a l 操作实现: \red{条件变量Wait和Signal操作实现:} 条件变量WaitSignal操作实现:

    Class Condition {
    	int numWaiting = 0;			//等待的线程数
    	WaitQueue q;				//线程的等待队列
    }
    
    Condition::Wait(lock) {	//将一个线程加入到等待队列中
    	numWaiting++;
    	Add this thread t to q;
    	release(lock);
    	schedule();
    	require(lock);
    }
    
    Condition::Signal() {		//唤醒一个在等待队列中等待的线程
    	if(numWaiting > 0) {
    		Remove a thread t from q;
    		wakeup(t);
    		numWaiting--;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    管程解决消费者——生产者问题 \color{purple}{管程解决消费者——生产者问题} 管程解决消费者——生产者问题

    classBoundedBuffer {				//字节缓冲区类构成
    	...
    	Lock lock;						//共享锁变量
    	int count = 0;					//线程中的数据数
    	Condition notFull , notEmpty;			//条件变量
    }
    
    BoundedBuffer::Deposit(e) {			//存储操作
    	lock -> Acquire();			//索要lock变量,锁住临界区
    	while(count == n)			//如果数据已经满了,当前生产者的存放进程进入等待状态
    		notFull.Wait(&lock);
    	Add c to the buffer;			//存放数据
    	count++;
    	notEmpty.Signal();			//Signal操作通知消费者可以进行取出数据了
    	lock -> Release();			//释放其他线程访问临界区的资格
    }
    
    BoundedBuffer::Remove(c) {				//消费者取出资源操作
    	lock -> Acquire();						//索要访问临界区资源的权限
    	while(count == 0)			//临界区没有资源了,当前消费者取出资源的操作进入等待状态
    		notEmpty.Wait(&lock);			
    	Remove c from buffer;
    	count--;
    	notFull.Signal();				//向生产者发出信号,表示生产者可以继续存放数据了
    	lock -> Release();				//释放临界区访问权限
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    线程同步经典问题 \purple{线程同步经典问题} 线程同步经典问题

    ①. 哲学家用餐问题: \red{哲学家用餐问题:} 哲学家用餐问题:
    在这里插入图片描述 思路: \color{olive}{思路:} 思路:

    1. 将每个哲学家的当前状态表示为临界区的临界资源。在这里插入图片描述

    2. 每个哲学家访问临界资源的时候,应该是互斥的( 进程互斥 \color{blue}{进程互斥} 进程互斥)。
      在这里插入图片描述

    3. 一个哲学家吃饱之后,可能会唤醒它的左邻右舍,两者之间存在着同步关系( 进程同步 \color{blue}{进程同步} 进程同步)。
      在这里插入图片描述

    伪代码实现: \color{orange}{\huge{伪代码实现:}} 伪代码实现:
    在这里插入图片描述1. t a k e _ f o r k s take\_forks take_forks函数:
    在这里插入图片描述2. t e s t t _ t a k e _ l e f t _ r i g h t _ f o r k s testt\_take\_left\_right\_forks testt_take_left_right_forks函数
    在这里插入图片描述3. p u t _ f o r k s put\_forks put_forks函数
    在这里插入图片描述

  • 相关阅读:
    JavaScript中内置对象的方法总结
    python安装davisinteractive模块
    C语言学习笔记
    FlyBird
    【Nginx32】Nginx学习:随机索引、真实IP处理与来源处理模块
    xss之DOM破坏
    nginx + tomcat 搭建负载均衡、动静分离(tomcat多实例)
    《图解Pandas》内容汇总-20220822
    Cadence Allegro学习笔记【原理图篇】
    Java进阶架构实战——Redis在京东到家的订单中的使用
  • 原文地址:https://blog.csdn.net/qq_51542797/article/details/127134445