linux网络编程-posix条件变量(40)

举一个列子来说明条件变量:

假设有两个线程同时访问全局变量n,初始化值是0,

一个线程进入临界区,进行互斥操作,线程当n大于0的时候才执行下面的操作,如果n不大于0,该线程就一直等待。

另外一个线程也是进入临界区,修改n的值,当修改了n的值后,需要向等待中的线程发送通知,修改了n的值。但是现在存在这样的一个问题:当第一个线程进入临界区的时候,第一个线程一直处在等待n改变的状态,第二个线程是无法进入临界区的修改n的值的,这样第一个线程

就处于死锁了。上面这个问题可以时候条件变量来解决。

条件变量也可以用于生产者和消费者的状态:

假设一个线程访问一个队列该队列是空的,那么该线程只能处于阻塞状态,直到其他线程将一个产品添加到队列中,发现通知通知等待的线程

对于等待条件的代码,首先需要锁定互斥量,因为这个条件变量是多个线程都可以同时访问的,所以需要进行互斥,这里使用while 等待只要条件为假,该线程就一直处于等待状态

对于给条件发现信号的代码:首先也是需要进行互斥,然后设置条件为真,然后给等待的线程发一个通知

下面我们使用条件变量来进行生产者与消费者的问题

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

pthread_cond_init函数可以用来初始化一个条件变量。他使用变量attr所指定的属性来初始化一个条件变量,如果参数attr为空,那么它将使用缺省的属性来设置所指定的条件变量。

pthread_cond_destroy函数可以用来摧毁所指定的条件变量,同时将会释放所给它分配的资源。调用该函数的进程也并不要求等待在参数所指定的条件变量上。

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

cond 条件变量

mutex 互斥锁

 第一个参数*cond是指向一个条件变量的指针。第二个参数*mutex则是对相关的互斥锁的指针。

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

参数*cond是对类型为pthread_cond_t 的一个条件变量的指针。当调用pthread_cond_signal时一个在相同条件变量上阻塞的线程将被解锁。如果同时有多个线程阻塞,则由调度策略确定接收通知的线程。如果调用pthread_cond_broadcast,则将通知阻塞在这个条件变量上的所有线程。一旦被唤醒,线程仍然会要求互斥锁。如果当前没有线程等待通知,则上面两种调用实际上成为一个空操作。如果参数*cond指向非法地址,则返回值EINVAL。

#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m) 
        do 
        { 
                perror(m); 
                exit(EXIT_FAILURE); 
        } while(0)

#define CONSUMERS_COUNT 1
#define PRODUCERS_COUNT 1
#define BUFFSIZE 10

//首先定义一个条件变量
pthread_cond_t g_cond; 

unsigned short in = 0;
unsigned short out = 0;
unsigned short produce_id = 0;
unsigned short consume_id = 0;
int nReady = 0;//表示当前产生的数目的计数器 
pthread_mutex_t g_mutex;

pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT];

void *consume(void *arg)
{
    int i;
    int num = (int)arg;
    while (1)
    {
        // 首先需要进行互斥
        pthread_mutex_lock(&g_mutex);
        while(nReady == 0){//等待产品不为空 
        //条件变量需要和互斥锁进行配合使用 
        printf("consume thread_id %d now is waiting for .....
",num); 
            pthread_cond_wait(&g_cond,&g_mutex); 
        }
        printf("consume thread_id %d now is begin consume product 
",num); 
        //消费产品,将产品数据减一
         nReady = nReady -1; 
        pthread_mutex_unlock(&g_mutex);
        sleep(1); 
      
    }
    return NULL;
}

void *produce(void *arg)
{
    int num = (int)arg;
    int i;
    while (1)
    {
            // 首先需要进行互斥
        pthread_mutex_lock(&g_mutex);
        //开始生产产品
         nReady = nReady +1;
         //发起通知
        printf("producter thread_id %d now is singal for consume .....
",num); 
        pthread_cond_signal(&g_cond);
        pthread_mutex_unlock(&g_mutex);
        sleep(3);
    }
    return NULL;
}

int main(void)
{
    int i;
    

    //初始化一个互斥锁 
    pthread_mutex_init(&g_mutex, NULL);
    //初始化条件变量
    pthread_cond_init(&g_cond,NULL);
     

    for (i = 0; i < CONSUMERS_COUNT; i++)
        pthread_create(&g_thread[i], NULL, consume, (void *)i);

    for (i = 0; i < PRODUCERS_COUNT; i++)
        pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);

    for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; i++)
        pthread_join(g_thread[i], NULL);

    //销毁互斥锁 
    pthread_mutex_destroy(&g_mutex);
    //销毁条件变量
    pthread_cond_destroy(&g_mutex); 

    return 0;
}

编译下代码:

gcc product.c -o product -lpthread

运行代码:

 ./product 

代码中生产者和消费者都只有一个线程

我们来看程序运行的结果:

pthread_cond_wait前要先加锁
     pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
     pthread_cond_wait被激活后会再自动加锁 

pthread_cond_wait的内部实现流程:

1、第一步首先对互斥锁进行解锁

2、第二步等待其他线程改变条件变量

3、然后自动加锁后退出等待

原文地址:https://www.cnblogs.com/kebibuluan/p/7115723.html