Linux线程同步(3)生产者与消费者、条件变量与信号量

本文介绍了生产者-消费者问题的经典并发编程模型,探讨了互斥锁、信号量和条件变量在解决数据共享和同步问题中的应用,以及如何避免死锁和饥饿现象。通过实例展示了使用链表和线程池实现的基本生产者-消费者模型,并讨论了不同同步机制的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.生产者与消费者的概念

生产者与消费者(Producer-Consumer)问题,是一个经典的并发编程问题。在这个问题中,涉及到两类进程:生产者和消费者生产者负责生产数据,并将其放入一个缓冲区中消费者则从缓冲区中取出数据并消费

生产者和消费者通常是并发运行的,即它们可能在不同的时间点执行。生产者可能会在生产数据后立即放入缓冲区,而消费者可能会在稍后的时间点从缓冲区中取出数据。因此,需要一种机制来同步生产者和消费者的行为,以确保缓冲区不会溢出(即生产者不会在缓冲区满时继续生产数据)或下溢(即消费者不会在缓冲区空时尝试取出数据)。

生产者与消费者模型是一个经典的并发编程模型,用于解决多线程或进程间的数据共享和同步问题。该模型涉及两类对象:生产者和消费者,以及一个共享的数据缓冲区

生产者:负责生产数据,并将这些数据放入共享缓冲区中。生产者可以在任何时间点生产数据,并将其添加到缓冲区,供消费者使用。

消费者:负责从共享缓冲区中取出数据,并进行消费(或处理)。消费者可以在任何时间点从缓冲区中取出数据进行处理。

共享缓冲区:是一个中介,用于存储生产者生产的数据,供消费者使用。这个缓冲区通常是有限的,因此生产者和消费者需要一种机制来同步他们的行为,以防止缓冲区溢出(生产者生产的数据太多,消费者来不及消费)或下溢(消费者消费的数据太多,生产者来不及生产)。

生产者-消费者模型中的关键概念和原则包括:

  1. 互斥:生产者和消费者需要互斥地访问共享缓冲区,以防止数据竞争和不一致的状态。这通常通过使用互斥锁(mutex)或其他同步机制来实现。
  2. 同步:生产者和消费者之间的同步是确保数据正确流动的关键。生产者不能在缓冲区满时继续生产数据,而消费者也不能在缓冲区空时尝试取出数据。这可以通过使用信号量(semaphores)、条件变量(condition variables)或其他同步机制来实现。
  3. 死锁和饥饿:在生产者-消费者模型中,需要特别注意避免死锁和饥饿问题。死锁是指多个线程或进程互相等待对方释放资源,导致无法继续执行。饥饿是指某些线程或进程长时间得不到所需的资源,而其他线程或进程却能得到足够的资源。为了避免这些问题,需要合理设计同步机制,并确保资源的公平分配。

初步实现生产者和消费者模型:

使用链表的数据结构,生产者生产数据,即不断地创建新节点,然后添加到链表中

首先创建链表的数据结构,然后初始化一个头节点。

为了生产者和消费者需要互斥地访问共享缓冲区,那么创建一个互斥量 mutex

// 创建一个互斥量
pthread_mutex_t mutex;

//链表节点数据结构
struct Node{
    int num;
    struct Node *next;
};

// 头结点
struct Node * head = NULL;

生产者采用头插法向链表中添加数据

//生产者生产数据
void * producer(void * arg) {

    // 不断的创建新的节点,添加到链表中
    while(1) {
        pthread_mutex_lock(&mutex);
        struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand() % 1000; //随机值
        printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
        pthread_mutex_unlock(&mutex);
        usleep(100);
    }

    return NULL;
}

消费者需要读取缓冲区中的数据,然后删除节点,在删除的时候,为了避免溢出,需要先判断链表中是否还有节点可以删除,即if(head != NULL) 

//消费者,取内容
void * customer(void * arg) {

    while(1) {
        pthread_mutex_lock(&mutex);
        // 保存头结点的指针
        struct Node * tmp = head;

        // 判断是否有数据
        if(head != NULL) {
            // 有数据
            head = head->next;
            printf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值