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

    你可能感兴趣的文章
    Navicat for MySQL 查看BLOB字段内容
    查看>>
    Neo4j电影关系图Cypher
    查看>>
    Neo4j的安装与使用
    查看>>
    Neo4j(2):环境搭建
    查看>>
    Neo私链
    查看>>
    nessus快速安装使用指南(非常详细)零基础入门到精通,收藏这一篇就够了
    查看>>
    Nessus漏洞扫描教程之配置Nessus
    查看>>
    Nest.js 6.0.0 正式版发布,基于 TypeScript 的 Node.js 框架
    查看>>
    NetApp凭借领先的混合云数据与服务把握数字化转型机遇
    查看>>
    NetBeans IDE8.0需要JDK1.7及以上版本
    查看>>
    netcat的端口转发功能的实现
    查看>>
    netfilter应用场景
    查看>>
    netlink2.6.32内核实现源码
    查看>>
    Netpas:不一样的SD-WAN+ 保障网络通讯品质
    查看>>
    NetScaler的常用配置
    查看>>
    netsh advfirewall
    查看>>
    NETSH WINSOCK RESET这条命令的含义和作用?
    查看>>
    Netty WebSocket客户端
    查看>>
    netty 主要组件+黏包半包+rpc框架+源码透析
    查看>>
    Netty 异步任务调度与异步线程池
    查看>>