博客
关于我
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_waitsem_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/

    你可能感兴趣的文章
    Pinia:$subscribe()的使用场景
    查看>>
    Pinpoint对Kubernetes关键业务模块进行全链路监控
    查看>>
    Pinterest 大规模缓存集群的架构剖析
    查看>>
    pintos project (2) Project 1 Thread -Mission 1 Code
    查看>>
    PinYin4j库的使用
    查看>>
    PIP
    查看>>
    pip install goose-extractor // SyntaxError: Missing parentheses in call to 'print'
    查看>>
    pip install mysqlclient报错
    查看>>
    pip install 出现报asciii码错误的解决
    查看>>
    pip throws TypeError: parse() got an unexpected keyword argument ‘transport_encoding‘ 在尝试安装新软件包时
    查看>>
    pip 下载慢
    查看>>
    pip 升级报错AttributeError: ‘NoneType’ object has no attribute ‘bytes’
    查看>>
    pip 安装opencv-python卡死
    查看>>
    pip 安装出现异常
    查看>>
    Pip 安装失败:需要 SSL
    查看>>
    Pip 安装挂起
    查看>>
    pip 或 pip3 为 Python 3 安装包?
    查看>>
    pip 文件损坏导致 pip无法使用 报错 ImportError: cannot import name 'main' from 'pip._int
    查看>>
    pip 无法从 requirements.txt 安装软件包
    查看>>
    pip/pip3更换国内源
    查看>>