Linux Posix线程条件变量

Posted 庖丁解牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux Posix线程条件变量相关的知识,希望对你有一定的参考价值。

生产者消费者模型
1.多个线程操作全局变量n,需要做成临界区(要加锁--线程锁或者信号量)
2.调用函数pthread_cond_wait(&g_cond,&g_mutex)让这个线程锁在某一个条件上等待
--pthread_cond_wait()函数的本质是①:拿到锁的线程,把锁暂时丢掉(解锁)②:线程休眠,进行等待③:线程等待通知,醒来继续执行(重新获得锁)
--这个pthread_cond_wait()函数是一个原子性操作
--注意:丢掉的锁可以被生产线程获得,也可以被消费线程获得,消费者线程获取会陷入等待,生产者线程获得会发送信号,唤醒消费者
3.pthread_cond_signal()生产者拿到锁,生产n,生产完成后,发送信号给消费者(唤醒消费者),生产者离开临界区
//生产者消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

//定义线程锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//定义线程条件锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//定义全局变量
int g_num = 0;

//定义消费者线程数量
#define Consume_Count 2

//定义生产者线程数量
#define Product_Count 4

//消费者线程
void *consume_run(void * arg)
{
    int tnum = (int) arg;
    while (1)
    {
        //因为多线程操作一个全局变量,需要做成临界区,枷锁
        pthread_mutex_lock(&mutex);
        printf("消费者%d进入临界区!\\n", tnum);
        //因为就算消费者被唤醒,也需要重新检查g_num的值,因为pthread_cond_wait()函数丢锁之后,可能被其他消费者获得
        //导致多个消费者线程都执行了pthread_cond_wait()函数陷入等待,
        //因为多个消费者线程被pthread_cond_signal()唤醒,但是资源只有一个,可能已经被其他消费者使用了
        //pthread_cond_wait()也有可能被其他信号唤醒
        //所以需要重新判断,而不能使用if
        while (g_num == 0)
        {
            printf("消费者%d开始等待!\\n", tnum);
            //在该线程锁上,进行条件等待
            pthread_cond_wait(&cond, &mutex);
            printf("消费者%d被唤醒!\\n", tnum);
        }
        //表示已经有资源了,可以消费了
        printf("消费者%d开始消费!\\n", tnum);
        g_num--;
        //解锁
        pthread_mutex_unlock(&mutex);
        sleep(3);
    }
    return NULL;
}

//生产者线程
void *product_run(void * arg)
{
    int tnum = (int) arg;
    while (1)
    {
        //当生产者大于消费者时,可能出现生产过多的商品,需要对商品的上限做一个限制,如果商品过多,需要让生产者睡眠
        //涉及到对全局变量的访问,需要加锁
        printf("现有资源个数%d!\\n",g_num);
        pthread_mutex_lock(&mutex);
        if (g_num > 20)
        {
            printf("生产者%d产品已经到达上限,进入睡眠!\\n", tnum);
            //如果生产产品大于上限,需要睡眠,但是睡眠前应该将锁释放.,留给消费者进行消费
            pthread_mutex_unlock(&mutex);
            sleep(3);
            continue;
        } else
        {
            printf("生产者%d生产产品!\\n", tnum);
            //如果生产产品小于上限,那么不需要生产者睡眠,接着生产产品
            g_num++;
            //发送条件信号
            printf("生产者%d发送条件信号!\\n", tnum);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
        }
        pthread_mutex_unlock(&mutex);
        sleep(2);
    }
    return NULL;
}

int main(int arg, char * args[])
{
    pthread_t thrs[Consume_Count + Product_Count];
    int i = 0;
    for (i = 0; i < Consume_Count; i++)
    {
        if (pthread_create(&thrs[i], NULL, consume_run, (void *) i) != 0)
        {
            printf("pthread_create() failed !\\n");
            return -1;
        }
    }
    for (i = 0; i < Product_Count; i++)
    {
        if (pthread_create(&thrs[i + Consume_Count], NULL, product_run,
                (void *) i) != 0)
        {
            printf("pthread_create() failed !\\n");
            return -1;
        }
    }
    //join
    for (i = 0; i < Consume_Count + Product_Count; i++)
    {
        if (pthread_join(thrs[i], NULL) != 0)
        {
            printf("pthread_join() failed !\\n");
            break;
        }
    }
    printf("进程推出了!\\n");
    return 0;
}

 编程遇到的问题

关于生产者消费者模型,生产者加锁通知消费者消费,释放锁,消费者捕捉到信号,wait状态被打断(存在虚假唤醒的情况--很普遍,因此需要用一个锁保护变量加以标识),醒来的消费者拿到锁,但是这个过程不是原子的,意思是假如生产者发信号后,解锁,然后由于某种业务场景,继续获取锁,因为生产者发送信号需要时间,存在一种特殊的情况,生产者发送信号了,后面生产者仍然于消费者wait捕捉信号之前拿到锁,假设存在某种变量是消费者wait之后修改的,而生产者需要加锁判断的,此时会产生问题,生产者判断的变量还没有被消费者所修改,从而引发业务上的错误,我想强调的一点是,生产者发送信号到消费者接收到信号这期间是有延迟的,开发中需要注意到这个问题

以上是关于Linux Posix线程条件变量的主要内容,如果未能解决你的问题,请参考以下文章

Linux Qt使用POSIX多线程条件变量互斥锁(量)

C POSIX 线程的互斥体/条件的工作方式因变量所在的位置而异

生产者-消费者问题:介绍POSIX线程的互斥量和条件变量的使用

POSIX条件变量

Linux 多线程编程(二)2019-08-10

转载同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)