《Linux从练气到飞升》No.30 深入理解 POSIX 信号量与生产消费模型

简介: 《Linux从练气到飞升》No.30 深入理解 POSIX 信号量与生产消费模型

前言

多线程编程领域,理解 POSIX 信号量的概念和相关函数是至关重要的。POSIX 信号量作为一种重要的同步原语,可以帮助我们在多线程环境中实现线程之间的协调与同步,从而确保数据的一致性和避免竞争条件的发生。

本篇博客旨在深入探讨 POSIX 信号量的基本概念和相关函数,帮助读者全面理解这一关键的并发编程工具。通过本文的学习,读者将能够掌握如何灵活地运用 POSIX 信号量来构建并发程序,提高程序的性能和可靠性。让我们一起深入探索 POSIX 信号量的奥秘,为多线程编程的世界增添新的智慧与力量。

1 POSIX信号量相关概念

POSIX信号量是什么?

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

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

当多个线程想要获取信号量的时候,都会对信号量当中的资源计数器进行减一操作。

如果初始化信号量的资源计数器的值为1表示当前只有一个资源,这就意味着只有一个线程在同一时刻可以获取到信号量。

如果想要实现线程同步,初始化信号量的资源计数器的值就不必为1了,它可以根据需要设置

  • 如果大于0则表示还有多少资源可以使用
  • 等于0则表示没有资源可以使用
  • 小于0则表示有多少线程在等待资源。

2 POSIX信号量相关函数

  1. 信号量初始化
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
  sem_t: 信号量的类型
  sem: 传入待要初始化的信号量
  pshared: 0 表示线程间共享,非0表示进程间共享
  value:信号量初始值
  1. 信号量销毁
int sem_destroy(sem_t *sem);
参数:
  sem:待销毁的信号量
  1. 信号量等待
功能:等待信号量,会将信号量的值减1
  int sem_wait(sem_t *sem); //P()
  1. 信号量发布
功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
  int sem_post(sem_t *sem);//V()

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

  • 上一个生产者-消费者的例子是基于queue的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序(POSIX信号量)。

  • 环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态

但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程

实现代码如下:

RingQueue.cc

#pragma
#include<iostream>
#include<unistd.h>
#include<vector>
#include<semaphore.h>
#include<stdlib.h>
#define NUM 10
class RingQueue
{
private:
    std::vector<int> v;
    int _cap;//容量
    sem_t sem_product;//生产者
    sem_t sem_consume;//消费者
    int p_index;//生产者索引
    int c_index;//消费者索引
public:
    RingQueue(int cap=NUM)
        :_cap(cap),v(cap)
    {
        sem_init(&sem_product,0,cap);
        sem_init(&sem_consume,0,0);
        p_index = 0;
        c_index = 0;
    }
    ~RingQueue(){
        sem_destroy(&sem_product);
        sem_destroy(&sem_consume);
    }
    void put(const int&in){
        sem_wait(&sem_product);
        v[p_index] = in;
        p_index++;
        p_index = p_index%NUM;
        sem_post(&sem_consume);
    }
    void get(int &out){
        sem_wait(&sem_consume);
        out = v[c_index];
        c_index++;
        c_index = c_index%NUM;
        sem_post(&sem_product);
    }
};

main.cc

#include"RingQueue.cc"
using namespace std;
void* Consumer(void* arg){
    RingQueue *bq = (RingQueue*)arg;
    int data;
    while(1){
        bq->get(data);
        cout<<"I am "<<pthread_self()<<" is consumer : "<<data<<endl;
    }
}
void* Product(void* arg){
    RingQueue* bq = (RingQueue*)arg;
    srand((unsigned int)time(NULL));
    while(1){
        int data = rand()%100;
        bq->put(data);
        cout<<"I am "<<pthread_self()<<" is product "<<data<<endl;
        sleep(1);
    }
}
int main()
{
    RingQueue* pq = new RingQueue();
    pthread_t c;
    pthread_t p;
    pthread_create(&c,NULL,Consumer,(void*)pq);
    pthread_create(&p,NULL,Product,(void*)pq);
    pthread_join(c,NULL);
    pthread_join(p,NULL);
    return 0;
}

makefile

main:main.cc
  g++ -o $@ $^ -lpthread
.PHONY:
clean:
  rm -f main

结果:

可以观察到生产一个消费一个

相关文章
|
1月前
|
Linux C++
LInux下Posix的传统线程示例
LInux下Posix的传统线程示例
20 1
|
2月前
|
Linux C语言 SoC
嵌入式linux总线设备驱动模型分析
嵌入式linux总线设备驱动模型分析
33 1
|
3月前
|
监控 网络协议 Linux
Linux多路转接or多路复用模型
【2月更文挑战第5天】
36 1
|
10天前
|
网络协议 Linux Shell
【linux网络(一)】初识网络, 理解四层网络模型
【linux网络(一)】初识网络, 理解四层网络模型
|
2月前
|
消息中间件 Linux
Linux进程间通信(IPC)教程 Linux共享内存介绍:介绍POSIX共享内存的基本概念、用途和编程实践
Linux进程间通信(IPC)教程 Linux共享内存介绍:介绍POSIX共享内存的基本概念、用途和编程实践
25 2
|
2月前
|
存储 Linux 程序员
Linux进程间通信(IPC)教程 Linux信号量:讲解POSIX信号量在Linux系统进程间通信中的编程实践
Linux进程间通信(IPC)教程 Linux信号量:讲解POSIX信号量在Linux系统进程间通信中的编程实践
21 1
|
2月前
|
消息中间件 Linux API
Linux进程间通信(IPC) Linux消息队列:讲解POSIX消息队列在Linux系统进程间通信中的应用和实践
Linux进程间通信(IPC) Linux消息队列:讲解POSIX消息队列在Linux系统进程间通信中的应用和实践
28 1
Linux进程间通信(IPC) Linux消息队列:讲解POSIX消息队列在Linux系统进程间通信中的应用和实践
|
3天前
|
SQL 缓存 监控
|
3天前
|
前端开发 Linux Shell