博客
关于我
Linux系统编程40:多线程之基于环形队列的生产者与消费者模型
阅读量:207 次
发布时间:2019-02-28

本文共 2833 字,大约阅读时间需要 9 分钟。

什么是信号量

在处理多线程程序时,临界区是需要保护的关键资源,但单独使用锁会导致效率低下。为了优化资源使用效率,信号量(Semaphore)提供了一种更灵活的资源管理机制。信号量可以看作是一个计数器,用于跟踪临界资源的可用数量。它通过两个操作——P(请求)和V(释放)来管理资源的分配和回收。

在传统的锁机制中,只能允许一个线程进入临界区,这限制了资源利用的潜力。信号量通过将临界区划分为多个独立的区域来解决这个问题。每个区域可以允许一个线程进入,从而提高整体的资源利用率。然而,如何处理超过指定区域数量的线程请求?这是信号量的核心作用。

信号量由两个部分组成:一个值(通常用S表示)和一个指针(用P指向等待该信号量的线程)。S的值反映了临界资源的状态:

  • 如果S ≥ 0,表示有相应数量的临界资源可用。
  • 如果执行P操作,请求分配一个资源,S会减一;如果S < 0,表示没有可用资源,此时S的绝对值表示等待该资源的线程数。请求者必须等待其他线程释放资源后才能继续运行。
  • 执行V操作时,释放一个资源,S会加一。

信号量的基本操作

在使用信号量之前,需要创建一个sem_t类型的变量。具体步骤如下:

  • 包含必要的头文件:
  • #include 
    1. 初始化信号量:使用sem_init函数初始化信号量。该函数的参数包括信号量变量、共享标志和初始值。
    2. int sem_init(sem_t* sem, int pshared, unsigned int value);
      • pshared一般设置为0,表示信号量仅在当前进程中使用。
      • value指定信号量的初始值。
      1. 销毁信号量:当信号量不再需要时,使用sem_destroy函数销毁它。
      2. int sem_destroy(sem_t* sem);
        1. 信号量操作接口:POSIX信号量定义了sem_wait和sem_post两个函数,分别对应P操作和V操作。
        2. int sem_wait(sem_t* sem);int sem_post(sem_t* sem);

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

          传统的阻塞队列模型中,生产者和消费者是串行运行的,这限制了它们的并行效率。环形队列(Circular Queue)通过并行化生产者和消费者来解决这一问题。每个生产者负责向队列中添加数据,每个消费者负责从队列中读取数据。

          在这种模型中,生产者和消费者都需要信号量来管理资源:

          • 消费者关心队列中是否有数据(P操作)。
          • 生产者关心队列中是否有空格(P操作)。

          通过信号量,生产者和消费者可以在不竞争的情况下并行运行。当队列为空时,消费者会被阻塞,直到生产者添加数据。当队列满时,生产者也会被阻塞,直到消费者释放空格。

          代码实现

          #include 
          #include
          #include
          #include
          using namespace std;class CircularQueue {private: vector
          v; int _cap; sem_t data; sem_t blank; int com_index; int pro_index;public: CircularQueue(int cap = 10) : _cap(cap) { v.resize(_cap); sem_init(&data, 0, 0); sem_init(&blank, 0, _cap); com_index = 0; pro_index = 0; } ~CircularQueue() { sem_destroy(&data); sem_destroy(&blank); } void Put(const int& val) { sem_wait(&blank); v[pro_index] = val; pro_index++; pro_index %= _cap; sem_post(&data); } void Get(int& val) { sem_wait(&data); val = v[com_index]; com_index++; com_index %= _cap; sem_post(&blank); }};

          测试程序

          #include "circular_queue.hpp"#include 
          CircularQueue* bq = new CircularQueue(10);void* consumer_run(void* arg) { while (1) { int data; bq->Get(data); printf("%s拿到数据:%d\n", (char*)arg, data); }}void* productor_run(void* arg) { int cout = 1; while (1) { if (cout == 11) { cout = 1; } bq->Put(cout); printf("%s放进数据:%d\n", (char*)arg, cout); cout++; sleep(1); }}int main() { pthread_t con, pro; pthread_create(&con, nullptr, consumer_run, (void*)"消费者"); pthread_create(&pro, nullptr, productor_run, (void*)"生产者"); pthread_join(con, nullptr); pthread_join(pro, nullptr); delete bq; return 0;}

          ###效果展示

          在本例中,生产者以较慢的速度工作(每秒生产一个数据),而消费者可以并行处理多个数据。这样可以避免生产者成为瓶颈,提升整体系统的效率。通过信号量的管理,生产者和消费者能够在不竞争的情况下高效运行。

    转载地址:http://visi.baihongyu.com/

    你可能感兴趣的文章
    Python:如何以非阻塞方式读取子进程的标准输出
    查看>>
    Python:如何使用 plotly 制作阴影区域或交替背景颜色?
    查看>>
    Python:如何根据日期时间获取值的计数
    查看>>
    python:如何检测串行 COM 上的设备名称/ID
    查看>>
    Python:如何模拟 datetime.utcnow()?
    查看>>
    Python:如果DataFrames之间的其他值匹配,则对DataFrame中的值求和
    查看>>
    Python:字典思维
    查看>>
    Python:将 HEX 字符串转换为字节
    查看>>
    Python:将列表与范围列表合并
    查看>>
    Python:将列表列表初始化为一定大小,
    查看>>
    Python:带有生成器的给定集合的幂集
    查看>>
    Python:按字符的位置拆分字符串
    查看>>
    Python:替换嵌套字典中的值
    查看>>
    Python:爬取数据出现response.status_code为403解决方法
    查看>>
    Python:爬取数据出现response.status_code为418解决方法
    查看>>
    Python:生成“元组集“;来自“元组列表“;不考虑顺序
    查看>>
    Python:生成具有趋势的随机时间序列数据(例如周期性、指数衰减等)
    查看>>
    Python:用渐进式数字重命名列表中的重复项而不对列表进行排序
    查看>>
    Python:矩形参数无效
    查看>>
    Python:语法进阶
    查看>>