博客
关于我
Linux多线程实践(5) --Posix信号量与互斥量解决生产者消费者问题
阅读量:793 次
发布时间:2023-02-03

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

Posix信号量

Posix信号量是一种进程间同步的机制,广泛应用于多线程程序的通信与调度。Posix信号量与System V信号量类似,主要通过信号量的名字进行操作。名字通常以"/somename"的形式标识,并且只能有一个实例,且名字长度不能超过NAME_MAX-4(即251个字符)。

Posix有名信号量的创建与管理主要通过以下函数完成:

  • sem_open:用于打开或创建有名信号量。
  • sem_close:用于关闭已打开的信号量。
  • sem_unlink:用于删除已创建的信号量。
  • sem_wait:用于等待信号量状态改变(P操作)。
  • sem_post:用于发送信号量变化通知(V操作)。

互斥锁

互斥锁是一种简单而高效的互斥机制,用于保护共享资源对原子操作的正确执行。互斥锁在多线程环境中非常有用,确保在同一时间只有一条线程能够访问共享资源。

Posix互斥锁的实现主要通过以下函数完成:

  • pthread_mutex_init:用于初始化互斥锁。
  • pthread_mutex_lock:用于上锁互斥锁。
  • pthread_mutex_trylock:用于非阻塞上锁。
  • pthread_mutex_unlock:用于解锁互斥锁。
  • pthread_mutex_destroy:用于销毁互斥锁。

Posix互斥锁可以分为快速互斥锁(默认)、递归互斥锁和检错互斥锁。快速互斥锁是最常用的版本,它在上锁时会导致等待直到互斥锁被释放。递归互斥锁允许线程在拥有互斥锁的同时递归加锁,而检错互斥锁则是非阻塞的版本,它会立即返回错误信息。

生产者与消费者问题

在Posix系统中,生产者与消费者问题通常通过信号量和互斥锁来解决。生产者负责向缓冲区中添加产品,消费者则负责从缓冲区中取走产品。为了确保缓冲区的正确使用,生产者和消费者需要对缓冲区进行互斥访问,同时确保缓冲区的充足空间和非空状态。

以下是一个典型的解决方案:

Storage类设计

class Storage {public:    Storage(unsigned int _bufferSize);    ~Storage();    void consume(int id);    void produce(int id);    void display(bool isConsumer = false);private:    unsigned int buffSize;    int *m_storage;    unsigned short int in;    unsigned short int out;    unsigned int product_number;    sem_t sem_full;    sem_t sem_empty;    pthread_mutex_t mutex;};

类实现

Storage::Storage(unsigned int _bufferSize) : buffSize(_bufferSize), in(0), out(0), product_number(0) {    m_storage = new int[buffSize];    for (unsigned int i = 0; i < buffSize; ++i)         m_storage[i] = -1;    sem_init(&sem_full, 0, 0);    sem_init(&sem_empty, 0, buffSize);    pthread_mutex_init(&mutex, NULL);}Storage::~Storage() {    delete []m_storage;    pthread_mutex_destroy(&mutex);    sem_destroy(&sem_empty);    sem_destroy(&sem_full);}

生产者与消费者代码

void Storage::produce(int id) {    printf("producer %d is waiting storage not full\n", id);    sem_wait(&sem_empty);    pthread_mutex_lock(&mutex);    m_storage[in] = product_number;    in = (in + 1) % buffSize;    pthread_mutex_unlock(&mutex);    sem_post(&sem_full);    sleep(1);}void Storage::consume(int id) {    printf("consumer %d is waiting storage not empty\n", id);    sem_wait(&sem_full);    pthread_mutex_lock(&mutex);    m_storage[out] = -1;    out = (out + 1) % buffSize;    pthread_mutex_unlock(&mutex);    sem_post(&sem_empty);    sleep(1);}

主控线程

int main() {    int nProducer = 1;    int nConsumer = 2;    cout << "please input the number of producer: ";    cin >> nProducer;    cout << "please input the number of consumer: ";    cin >> nConsumer;    cout << "please input the size of buffer: ";    int size;    cin >> size;    storage = new Storage(size);    pthread_t *thread = new pthread_t[nProducer + nConsumer];    for (int i = 0; i < nConsumer; ++i) {        pthread_create(&thread[i], NULL, consumer, new int(i));    }    for (int i = 0; i < nProducer; ++i) {        pthread_create(&thread[nConsumer + i], NULL, producer, new int(i));    }    for (int i = 0; i < nProducer + nConsumer; ++i) {        pthread_join(thread[i], NULL);    }    delete storage;    delete []thread;}

完整源代码

完整的源代码如上所示。通过合理的信号量初始化、互斥锁管理和线程创建,确保了生产者与消费者问题的高效解决。该方案能够在多线程环境中正确地管理缓冲区,避免数据竞争和死锁问题。

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

你可能感兴趣的文章
Linux学习总结(57)——生产环境用户权限管理规范
查看>>
Linux学习总结(58)——生产环境运维故障处理指南
查看>>
Linux学习总结(5)——CentOS常用的目录文件操作命令
查看>>
Linux学习总结(5)——CentOS常用的目录文件操作命令
查看>>
Linux学习总结(60)——Linux系统常用命令速查手册
查看>>
Linux学习总结(61)——Ansible 快速入门学习
查看>>
Linux学习总结(62)——什么是堡垒机?为什么需要堡垒机?
查看>>
Linux学习总结(63)——CMDB 详细介绍:概念、架构、模型、表设计及开源选择
查看>>
Linux学习总结(64)——DBA常用的Linux命令汇总
查看>>
Linux学习总结(65)——Linux 服务器安全强化的七个步骤
查看>>
Linux学习总结(66)——CentOS7操作系统SSH安全加固
查看>>
Linux学习总结(67)——shell脚本中$0 $1 $# $@ $* $? $ 等总结
查看>>
Linux学习总结(68)——Linux 30年专访:Linus Torvalds谈Linux内核开发与Git
查看>>
Linux学习总结(69)——Linux 生成随机数的6种方法
查看>>
Linux学习总结(6)——CenterOS7安装mysql5.5的方法
查看>>
Linux学习总结(6)——CenterOS7安装mysql5.5的方法
查看>>
Linux学习总结(70)——Bash 脚本中常用的内置变量汇总
查看>>
Linux学习总结(71)——Linux 管理面板哪家强?云帮手、APPNODE 还是宝塔?
查看>>
Linux学习总结(72)——Linux系统安全加固
查看>>
Linux学习总结(73)——Linux高频命令大总结
查看>>