美文网首页
信号量、互斥量

信号量、互斥量

作者: 王王王王王景 | 来源:发表于2019-07-15 09:13 被阅读0次

信号量

线程的信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。
如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。
信号量一般常用于保护一段代码,使其每次只被一个执行线程运行。信号量是用来调协线程对共享资源的访问的。
信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。

形象讲解:

如何理解这个信号量?为嘛信号量是这个东西?想想互斥量 mutex,相当于一把锁,如果一个人来了 lock 一下,其他人进不去了;最初的人 unlock 了,又可以进一个人了,进去一个又 lock 住。如果 mutex 锁在 unlock 状态下叫做 1 的话,lock 状态叫 0;1 实际反映的是锁的数量 !现在可以有多把锁,数量 count 最初为 n (可以设定);来一个人取一把锁(count减1),如果发现锁的数量(count)小于0,岂不言外之意,没有锁了 ?这样的情况下就要等(wait),或是说悬挂(suspend),或是说阻塞(block);直到神马时候呢?有人释放一把锁给 me 为止。当然了,如果最初就有锁的话,直接拿一把进去就可以了,O__O"…。me 的事情办完了,要出去了,还回一把锁(count加1),如果发现 count <=0,言外之意是神马呢?有人在等丫,O__O"…;好吧,me 把自己的锁给某一个人,唤醒一个等待的线程。当然如果最初就没有人等,me 就走 me 的,不用唤醒谁了。

ps:上面形象的讲解可以对照着下面的代码一起食用有助理理解

代码:

Semaphore.h

#pragma once
#include <mutex>
#include <condition_variable>
class Semaphore
{
public:
    explicit Semaphore(unsigned int count); //用无符号数表示信号量资源  
    ~Semaphore();

public:
    void wait();
    void signal();
private:
    int m_count; //计数器必须是有符号数  
    std::mutex m_mutex;
    std::condition_variable m_condition_variable;
};

Semaphore.cpp

#include "Semaphore.h"

Semaphore::Semaphore(unsigned int count) :m_count(count) {
}

Semaphore::~Semaphore()
{
}

void Semaphore::wait() {
    std::unique_lock<std::mutex> unique_lock(m_mutex);
    --m_count;
    while (m_count < 0) {
        m_condition_variable.wait(unique_lock);
    }
}

void Semaphore::signal() {
    std::lock_guard<std::mutex> lg(m_mutex);
    if (++m_count < 1) {
        m_condition_variable.notify_one();
    }
}

信号量的用处

sem.wait(); 是 P 操作;sem.signal(); 是 V 操作;
wait可以形象的看作是拿钥匙
signal可以看作是归还钥匙
count < 0的时候被阻塞
count = -3表示有三个线程在等待
count > 0的时候表示有空闲的钥匙可以被取走

信号量的用法大体来说和 mutex m 一样;m.lock(); 和 m.unlock(); 将需要保护的代码上下围起来;mutex 的特点就是 count = 1,典型的(二分)互斥量;而 sem(n) 则是数量有 n 个,比如 n 个坑,都占完的情况下,阻塞当前线程,否则可以进去(占个坑,O__O"…)。

include<iostream>
#include<thread>
#include"ilovers/semaphore"

std::mutex m;
ilovers::semaphore ba(0), cb(0), dc(0);

void a()
{
    ba.wait();// b --> a
    std::lock_guard<std::mutex>lock{m};
    std::cout <<"thread a"<<'\n';
}
void b()
{
    cb.wait();// c --> b
    std::lock_guard<std::mutex>lock{m};
    std::cout <<"thread b"<<'\n';
    ba.signal();// b --> a
}
void c()
{
    dc.wait();// d --> c
    std::lock_guard<std::mutex>lock{m};
    std::cout <<"thread c"<<'\n';
    cb.signal();// c --> b
}
void d()
{
    std::lock_guard<std::mutex>lock{m};
    std::cout <<"thread d"<<'\n';
    dc.signal();// d --> c
}

int main()
{
    std::thread th1{a}, th2{b}, th3{c}, th4{d};
    th1.join();
    th2.join();
    th3.join();
    th4.join();
    std::cout <<"thread main"<< std::endl;
    return0;
}

去掉上面的同步的信号量 ba、cb、dc 之后,程序的输出可能是 a b c d、a c d b、a c b d 等,几乎不可能是 d c b a 的顺序,然后加上三个控制顺序的信号量后,输出的顺序就是 d c b a main。对了 std::lock_guard 和 mutex 是为了互斥访问 std::cout 对象,这点要注意些(cout 一个共享对象/变量)。

参考网址:
https://blog.csdn.net/zdarks/article/details/46994767

信号量和资源关系

信号量的大小是根据资源来设定的

例:若有一售票厅只能容纳300人,当少于300人时,可以进入;否则,需在外等候。
若将每一个购票者作为一个进程,请用P(wait)、V(signal)操作编程,并写出信号量的初值。
(强调:只有一个购票窗口,每次只能为一位购票者服务)

分析:题中有两类资源,售票厅和售票窗口,售票厅可以容纳300人,窗口只能服务1个人。
用户到达后要想买到票,首先要进入售票厅,然后申请到窗口才行。因此用户需要获得两类资源才能成功购票。
好了,我们定义两个信号量一个是S1,代表售票厅还能容纳的人数,一个是S2,代表窗口,它们的初始值一个是300,一个是1。程序如下:

semophore S1=300,S2=1;
用户进程Pi
{
    Wait(S1);
    进入大厅;
    Wait(S2);
    窗口购票;
    退出购票窗口;
    signal(S2);
    退出大厅;
    Signal(S1);
}

注意,申请的时候能不能把S1,S2的顺序调换呢?

答案是不能的,因为如果某进程申请到了窗口(被叫号了),却无法进入大厅,那么其他人就无法购票,也就无法退出大厅,他也就一直进不去了。
另外在释放过程中,能不能把siganl(S2)放在程序的最后呢,答案是可以的,但是多进程并发时,资源释放的太晚是不是会影响计算机的效率呢。
参考:https://blog.csdn.net/yangkuiwu/article/details/52674809

相关文章

网友评论

      本文标题:信号量、互斥量

      本文链接:https://www.haomeiwen.com/subject/lglgkctx.html