Linux信号量:POSIX标准接口、实现生产者与消费者模型

简介: Linux信号量:POSIX标准接口、实现生产者与消费者模型

一、信号量简介


1.信号量


本质:内核中的一个计数器+等待队列


操作:PV操作


       P操作:判断计数器:


               大于0,则返回,返回前计数器-1;


               小于等于0则阻塞。


       V操作:计数器计数+1,唤醒一个阻塞的执行流


作用:实现进程或线程间的同步与互斥


       同步实现:计数器对资源进行计数


               获取资源前,进行P操作;


               产生一个资源,进行V操作;


       互斥实现:计数器置1,表示资源只有一个


               访问资源前,进行P操作;


               访问资源完毕后,进行V操作。


2.信号量与条件变量的区别


相同点:


       信号量与条件变量都可以实现同步


区别:


       信号量本身带有计数器,自身提供了资源获取条件判断的功能;


       条件变量,条件判断需要用户自己实现。


二、信号量标准接口POSIX


1.定义信号量


sem_t;


2.初始化信号量


int sem_init(sem_t *sem, int pshared, int val);


       sem:信号量变量;


       pshared: 0用于线程间;非0用于进程间;


       val:信号量的初始值;


返回值:


       成功,返回0;失败,返回-1。


3.P操作


int sem_wait(sem_t *sem);阻塞
int sem_trywait(sem_t *sem);非阻塞
int sem_timedwait(sem_t *sem);有时长限制的阻塞。


4.V操作


int sem_post(sem_t *sem);


5.释放信号量


int sem_destroy(sem_t *sem);


三、信号量实现生产者与消费者模型


1.信号量实现线程安全的环形队列


template <class T>
class CircularQueue {
    pricate:
        //实现环形队列
        std::vector<T> _array;
        int _capacity;
        int _front = 0;
        int _rear = 0;
        //实现同步
        sem_t _sem_idle;//对队列空闲空间计数
        sem_t _sem_data;//对有效数据节点计数
        //实现互斥
        sem_t _sem_lock;//实现互斥锁
};


2.完整代码


#include<iostream>
#include<cstdlib>
#include<vector>
#include<semaphore.h>
#include<pthread.h>
#define MAX_QUEUE 5
#define PRODUCER 4
#define CONSUMER 4
template <class T>
class CircularQueue {
  private:
    std::vector<T> _array;
    int _front;
    int _rear;
    int _capacity;
    sem_t _sem_idle;//对空闲空间计数
    sem_t _sem_data;//对数据空间计数
    sem_t _sem_lock;//实现互斥锁
  public:
    CircularQueue(int cap = MAX_QUEUE)
      : _capacity(cap) 
      ,_front(0)
      ,_rear(0)
      ,_array(cap) 
  {
    sem_init(&_sem_idle, 0, cap);
    sem_init(&_sem_data, 0, 0);
    sem_init(&_sem_lock, 0, 1);
  }
    ~CircularQueue() {
      sem_destroy(&_sem_idle);
      sem_destroy(&_sem_data);
      sem_destroy(&_sem_lock);
   }
    bool Push(const T data) {
     //1.P操作,对空闲空间计数进行判断
     sem_wait(&_sem_idle);
     //2.获取锁
     sem_wait(&_sem_lock);
     //3.放入数据
     _array[_front] = data;
     _front = (_front + 1) % _capacity;
     //4.解锁
     sem_post(&_sem_lock);
     //5.V操作,对数据空间进行计数+1
     sem_post(&_sem_data);
    }
    bool Pop(T *data) {
      //1.P操作,对数据空间计数进行判断
      sem_wait(&_sem_data); 
      //2.获取锁
      sem_wait(&_sem_lock);
      //3.获取数据
      *data = _array[_rear];
      _rear = (_rear + 1) % _capacity;
      //4.解锁
      sem_post(&_sem_lock);
      //5.V操作,对空闲空间计数+1
      sem_post(&_sem_idle);
    }
};
void *Consumer(void *arg) {
  CircularQueue<int> *p = (CircularQueue<int>*)arg;
  while (1) {
    int data;
    p -> Pop(&data);
    printf("Consumer get data: %d\n", data);
  }
}
void *Producer(void *arg) {
  CircularQueue<int> *p = (CircularQueue<int>*)arg;
  int data = 1;
  while (1) {
    p -> Push(data);
    printf("Producer put data: %d\n", data);
    ++data;
  }
}
void Test() {
  int ret;
  pthread_t con_tid[CONSUMER], pro_tid[PRODUCER];
  CircularQueue<int> q;
  //Create consumer threads 
  for (int i = 0; i < CONSUMER;  ++i) {
    pthread_create(&con_tid[i], NULL, Consumer, (void*)&q);
    if (ret != 0) {
      std::cout<<"Create consumer threads error!"<<std::endl;
      return;
    }
  }
  //Create producer threads 
  for (int i = 0; i < PRODUCER;  ++i) {
    pthread_create(&pro_tid[i], NULL, Producer, (void*)&q);
    if (ret != 0) {
      std::cout<<"Create producer threads error!"<<std::endl;
      return ;
    }
  }
  //wait threads 
  for (int i = 0; i < CONSUMER; ++i) {
    pthread_join(con_tid[i], NULL);
  }
  for (int i = 0; i < PRODUCER; ++i) {
    pthread_join(pro_tid[i], NULL);
  }
}
int main () {
  Test();
  return 0;
}


实现效果:


1.png

相关文章
|
1月前
|
消息中间件 Linux
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
119 48
|
2月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
170 20
|
3月前
|
Linux
【Linux】System V信号量详解以及semget()、semctl()和semop()函数讲解
System V信号量的概念及其在Linux中的使用,包括 `semget()`、`semctl()`和 `semop()`函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。
114 19
|
7月前
|
缓存 安全 Linux
Linux 五种IO模型
Linux 五种IO模型
|
6月前
|
Linux
linux内核 —— 读写信号量实验
linux内核 —— 读写信号量实验
|
7月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
109 0
|
Linux 消息中间件 存储
Linux有名信号量的创建(sem_open中name参数构造)【转】
转自:http://blog.csdn.net/gfeng168/article/details/40740865 版权声明:本文为博主原创文章,未经博主允许不得转载。 一、sem_open函数name参数的构造 Linux的有名信号量的创建是通过调用sem_open函数创建的,函数原型如下: 我刚开始用以为是想一般的指定一个路径名给sem_open中的name参数就可以了,可是我使用“/tmp/sharesem"给name参数确总是返回 no such file or diratory 的错误。
1782 0
|
25天前
|
Linux
Linux系统之whereis命令的基本使用
Linux系统之whereis命令的基本使用
63 23
Linux系统之whereis命令的基本使用
|
4月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
407 8
|
12天前
|
缓存 Ubuntu Linux
Linux中yum、rpm、apt-get、wget的区别,yum、rpm、apt-get常用命令,CentOS、Ubuntu中安装wget
通过本文,我们详细了解了 `yum`、`rpm`、`apt-get`和 `wget`的区别、常用命令以及在CentOS和Ubuntu中安装 `wget`的方法。`yum`和 `apt-get`是高层次的包管理器,分别用于RPM系和Debian系发行版,能够自动解决依赖问题;而 `rpm`是低层次的包管理工具,适合处理单个包;`wget`则是一个功能强大的下载工具,适用于各种下载任务。在实际使用中,根据系统类型和任务需求选择合适的工具,可以大大提高工作效率和系统管理的便利性。
76 25