19 POSIX信号量


POSIX信号量的概念

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步

信号量的本质是一个描述临界资源有效个数的计数器。

在这里插入图片描述

初始化、释放、等待、发布信号量

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

#include <semaphore.h> 
int sem_init(sem_t *sem, int pshared, unsigned int value); 

int sem_destroy(sem_t *sem);
pshared:0表示线程间共享,非零表示进程间共享 
value:信号量初始值

功能:等待信号量,会将信号量的值减1 
int sem_wait(sem_t *sem);

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1int sem_post(sem_t *sem);

基于环形队列的生产消费模型

之前的生产者和消费者的模型是基于队列queue的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序:

  • 环形队列采用数组模拟,用模运算来模拟环状特性。
  • 环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态。
  • 但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程。

在这里插入图片描述

代码实现

RingQueue.hpp:

#pragma once

#include<iostream>
#include<semaphore.h>
#include<vector>
#include<unistd.h>
#define NUM 10
class RingQueue{
private:
  std::vector<int> v;
  int max_cap;//最大个数
  sem_t sem_blank;//生产者
  sem_t sem_data;//消费者

  int c_index;//消费者索引下标
  int p_index;//生产者索引下标

  void P(sem_t &s){//信号量等待
    sem_wait(&s);
  }
  void V(sem_t &s){//信号量发布
    sem_post(&s);
  }


public:
  RingQueue(int _cap=NUM):max_cap(_cap),v(_cap){
    sem_init(&sem_blank,0,max_cap);
    sem_init(&sem_data,0,0);
    c_index=0;
    p_index=0;    
  }

  void Get(int& out){
    P(sem_data);//读一个数据,消费者的信号量计数器减一

    out=v[c_index];
    c_index++;
    c_index%=max_cap;//防止越界,构成环形队列
    V(sem_blank);//读一个数据,生产者的信号量计数器加一,说明队列有一个空位
  }
  void Put(const int&in){
    P(sem_blank);//入一个数据,生产者的信号量计数器减一,说明队列少了一个空位
    
    v[p_index]=in;
    p_index++;
    p_index%=max_cap;//防止越界,构成环形队列

    V(sem_data);//入一个数据,消费者的信号量计数器加一,说明队列有一个空位填了元素
  }

  ~RingQueue(){
    sem_destroy(&sem_data);
    sem_destroy(&sem_blank);
    c_index=0;
    p_index=0;
  }
};

main.cpp:

#include"RingQueue.hpp"
void*comsumer(void*ring_queue){

  RingQueue*rq=(RingQueue*)ring_queue;
  while(true){
    sleep(1);//让生产者先生产
    int data=0;
    rq->Get(data);
    std::cout<<"comsumer get data:"<<data<<std::endl;
  }


}
void*producer(void*ring_queue){

  RingQueue*rq=(RingQueue*)ring_queue;
  int count=1;
  while(true){
    rq->Put(count);
    count=count%5+1;

    std::cout<<"producer put!"<<std::endl;
  }
}

int main(){

  pthread_t c,p;

  RingQueue*rq=new RingQueue();
  pthread_create(&c,nullptr,comsumer,rq);
  pthread_create(&p,nullptr,producer,rq);


  pthread_join(c,nullptr);
  pthread_join(p,nullptr);
  delete rq;
}

在这里插入图片描述

由于消费者一开始睡眠1秒,因此生产者会一下生产满,后续当消费者消费一个,生产者就会再生产一个,一直保持队列为满的状态。


使用信号量实现共享内存的同步通信

  • server.cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include<unistd.h>

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};


void P(int semid,unsigned short num)
{
		struct sembuf set;
		set.sem_num = num; //信号量在数组里的序号
		set.sem_op = -1; //信号量的操作值
		set.sem_flg = SEM_UNDO; //信号量的操作标识
		semop(semid, &set,1);
}

void V(int semid,unsigned short num)
{
        struct sembuf set;
		set.sem_num = num;
        set.sem_op = 1;
        set.sem_flg = SEM_UNDO;
        semop(semid, &set,1);
 }
 
int main()
{
    key_t key1,key2;
    key1 = ftok(".",50);     //获取键值
    key2 = ftok(".",10);
    
    int shmid = shmget(key1,1024*4,IPC_CREAT|0666); //打开或者创建共享内存
    int semid = semget(key2,2,IPC_CREAT|0666);//打开或者创建信号量组
 
    char *shmaddr = shmat(shmid,0,0); //共享内存连接到当前进程的地址空间
    printf("shmat ok\n");       //表示连接成功
    printf("%d\n",semid);

    while(1)
    { 
        P(semid,1); //等待第二个信号量释放 
        printf("%s\n",shmaddr);  
        V(semid,0); //释放第一个信号量                                                  
    }

    shmdt(shmaddr);
    printf("quit\n");
    return 0;
}
  • client.cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include<unistd.h>

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};


void P(int semid,unsigned short num)
{
		struct sembuf set;
		set.sem_num = num; //信号量在数组里的序号
		set.sem_op = -1; //信号量的操作值
		set.sem_flg = SEM_UNDO; //信号量的操作标识
		semop(semid, &set,1);
}

void V(int semid,unsigned short num)
{
        struct sembuf set;
		set.sem_num = num;
        set.sem_op = 1;
        set.sem_flg = SEM_UNDO;
        semop(semid, &set,1);
 }
 
int main()
{
	key_t key1,key2;
    key1 = ftok(".",50);     //获取键值
    key2 = ftok(".",10);
    
    int shmid = shmget(key1,1024*4,IPC_CREAT|0666); //打开或者创建共享内存
    int semid = semget(key2,2,IPC_CREAT|0666);//打开或者创建信号量组
   	
   	union semun seminit; //信号量初始化
	seminit.val = 1; //第一个信号量设置为1
    semctl(semid,0,SETVAL,seminit);
    seminit.val = 0;//第二个信号量设置为0
    semctl(semid,1,SETVAL,seminit);
    char *shmaddr = shmat(shmid,0,0);  //共享内存连接到当前进程的地址空间
    printf("shmat ok\n");
    printf("%d\n",semid); 
    char c='a';    
    for(;c<='z';c++)
    {  
        sleep(1);
        P(semid,0); //给第一个信号量上锁
        //往共享内存中写入  
        shmaddr[c-'a']=c;  
        printf("%s\n",shmaddr);  
        V(semid,1); //释放第二个信号量
   }  
    shmdt(shmaddr);            //断开进程和内存的连接
    shmctl(shmid,IPC_RMID,0);  //删除共享内存段
    semctl(semid,0,IPC_RMID);   //删除信号量组
  	printf("quit\n");
    return 0;
}

在这里插入图片描述

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也要写bug、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值