【Linux】POSIX信号量

一. 什么是POSIX信号量?

POSIXSystem V都是可移植的操作系统接口标准,它们都定义了操作系统应该为应用程序提供的接口标准。

  • POSIX信号量和System V信号量作用相同,都是用于同步和互斥操作,以达到无冲突的访问共享资源目的。
  • System V版本的信号量只适用于实现进程间的通信,而POSIX版本的信号量主要用于实现线程之间的通信。

信号量(信号灯)本质是一个是用来对临界资源进行更细粒度地描述和管理的计数器。

二. 为什么要有POSIX信号量?

POSIX信号量主要用于实现线程间的同步。

三. POSIX信号量实现原理

信号量的结构如下:
在这里插入图片描述

  • count:记录还有多少小块的临界资源未被使用。
  • queue:当count为0时,其它未申请到信号量的线程的task_struct地址会被放到信号量等待队列中阻塞挂起。

信号量的PV操作

  • P操作:我们把申请信号量得操作称为P操作,申请信号量的本质就是申请获得整块临界资源中某小块资源的使用权限,当申请成功时临界资源中小块资源的数目应该减一,因此P操作的本质就是让count- -。
  • V操作:我们将释放信号量称为V操作,释放信号量的本质就是归还临界资源中某块资源的使用权限,当释放成功时临界资源中资源的数目就应该加一,因此V操作的本质就是让count++。

申请不到信号量的线程被阻塞挂起

count为0时,表示不允许其它线程再访问临界资源,这时其它申请信号量的线程会被阻塞到该信号量的等待队列中,直到其它线程释放信号量。

四. POSIX信号量接口函数

在这里插入图片描述

1. 创建、初始化信号量

信号量的类型是sem_t,我们可以根据这个类型自己定义信号量对象:
在这里插入图片描述
定义出信号量对象之后,必须用sem_init()函数来初始化这个信号量:
在这里插入图片描述
参数说明:

  • sem:信号量对象的地址。
  • pshared:0表示线程间共享,非零表示进程间共享。
  • value:信号量初始值,即count的大小。

函数说明:该函数主要用于设置信号量对象的基本属性。

2. 销毁信号量

在这里插入图片描述
函数说明:只需传入信号量对象的地址即可销毁该信号量。

3. 等待(申请)信号量

在这里插入图片描述
函数说明:传入信号量对象的地址用于申请该信号量,调用成功返回0,count- -;失败返回-1,count值不变。

4. 发布(释放)信号量

在这里插入图片描述
函数说明:传入信号量对象的地址用于释放该信号量,调用成功返回0,count++;失败返回-1,count值不变。

五. 信号量的应用

1. 二元信号量模拟互斥锁

count = 1时,说明整块临界资源作为一个整体使用而没有被切分管理,那么这个信号量对象就相当于是一把互斥锁,称为二元信号量。

下面我们用二元信号量模拟互斥锁完成黄牛抢票代码:

  • 在主线程中创建4个新线程去抢10张票。
  • 此时票是临界资源,我们用二元信号量对其进行保护。
  • 每个新线程抢票之前都要先申请二元信号量,没有申请到线程被阻塞挂起。
#include <iostream>    
#include <unistd.h>    
#include <pthread.h>                                                                                                  
#include <semaphore.h>    
using namespace std;    
    
// 封装一个自己的信号量类    
class MySem    
{
       
  public:    
    // 构造函数,用于初始化信号量对象,默认线程间通信,只需传入需要设置的count得值即可    
    MySem(size_t num)    
    {
       
      sem_init(&_sem, 0, num);    
    }    
    
    // 析构函数,销毁信号量对象    
    ~MySem()    
    {
       
      sem_destroy(&_sem);    
    }    
                                                                                                                         
    // 申请信号量
    void P()
    {
   
      sem_wait(&_sem);
    }

    // 释放信号量
    void V()
    {
   
      sem_post(&_sem);
    }

  private:
    // 成员变量是一个信号量对象
    sem_t _sem;
};

// 定义的全局对象
int count = 10;// 票数设为10张
MySem sem(1);// 一元信号量

// 新线程执行的抢票逻辑
void* GetTickets(void* arg)
{
   
  while(true)
  {
   
    size_t id = (size_t)arg;
    sem.P();
    if(count > 0)
    {
   
      usleep(1000);
      cout<<'['<<"thread "<<id<<']'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值