线程条件变量pthread_cond_t和线程条件锁详解

参考文章

条件变量常与互斥锁同时使用,达到线程同步的目的:条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。

APUE上,关于条件锁。其中有2条总结:

1.使用条件锁前必须先锁住对应的互斥锁。

2.条件锁进入阻塞(pthread_cond_wait)时自动解开对应互斥锁,而一旦跳出阻塞立即再次取得互斥锁,而这两个操作都是原子操作。

示例代码如下:

//互斥锁
pthread_mutex_t counter_lock;
//条件变量 
pthread_cond_t counter_nonzero;
int counter = 0;

void decrement_counter(void *argv)
{
    pthread_mutex_lock(&counter_lock);
    printf("thd1 decrement get the lock \n");
    while(counter == 0)
    {
        printf("thd1 decrement before cond_wait\n");
        pthread_cond_wait(&counter_nonzero, &counter_lock);
        /*
        运行到此处条件锁会自动以原子操作的方式将互斥锁解锁
        条件锁在收到pthread_cond_signal发出的信号后会尝试再次获取到互斥锁由于互斥锁已经被increment_counter函数持有
        因此decrement_counter函数所在的线程进入休眠状态等待increment_counter函数所在的线程解锁互斥锁
        当increment_counter函数所在的线程解锁互斥锁后decrement_counter函数所在的线程从休眠状态恢复开始运行
        */
        printf("thd1 decrement after cond_wait \n");
    }
    counter--;
    printf("thd1 decrement:counter = %d \n", counter);
    pthread_mutex_unlock(&counter_lock);
}

void increment_counter(void *argv)
{
    pthread_mutex_lock(&counter_lock);
    printf("thd2 increment get the lock\n");
    if(counter == 0)
    {
        printf("thd2 increment before cond_signal\n");
        pthread_cond_signal(&counter_nonzero);//发出条件变量信号
        printf("thd2 increment after  cond_signal\n");
    }
    counter++;
    printf("thd2 increment:counter = %d \n", counter);
    pthread_mutex_unlock(&counter_lock);
}

void test_pthread_cond(){
    printf("counter: %d\n", counter);
    pthread_t thd1, thd2;

    pthread_mutex_init(&counter_lock, NULL);
    pthread_cond_init(&counter_nonzero, NULL);

    int ret;
    printf("main1: before creating thd1 decrement \n");
    ret = pthread_create(&thd1, NULL, (void *)decrement_counter, NULL);//先创建的是等待线程,用pthread_cond_wait
    if(ret){
        perror("del:/n");
        return ;
    }
    printf("main2:thd1 is created, begin create thd2 increment \n");
    ret = pthread_create(&thd2, NULL, (void *)increment_counter, NULL);//后创建的是激活线程,用pthread_cond_signal
    if(ret){
        perror("inc: /n");
        return ;
    }
    printf("main3:after thd1 and thd2, begin sleep and exit!\n");
    sleep(3);
    return ;
}

/*
运行结果:
counter: 0
main1: before creating thd1 decrement 
main2:thd1 is created, begin create thd2 increment 
main3:after thd1 and thd2, begin sleep and exit!
thd1 decrement get the lock 
thd1 decrement before cond_wait
thd2 increment get the lock
thd2 increment before cond_signal
thd2 increment after  cond_signal
thd2 increment:counter = 1 
thd1 decrement after cond_wait 
thd1 decrement:counter = 0
*/
//互斥锁
pthread_mutex_t mutex;
//条件变量
pthread_cond_t cond_l;
int i = 1;

void* run(void *s)
{
    pthread_mutex_lock(&mutex);
    while(i == 1)
    {
        printf("线程%u进入等待状态\n", (unsigned int)pthread_self());
        pthread_cond_wait(&cond_l, &mutex);
    }
    printf("已经解开%u\n", (unsigned int)pthread_self());
    pthread_mutex_unlock(&mutex);
    i = 1;

    return ((void *) 1);
}

void testRun(){
    pthread_t pid1;
    pthread_t pid2;
    pthread_t pid3;
    pthread_t pid4;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond_l, NULL);

    pthread_create(&pid1, NULL, run, NULL );
    printf("new thread:%u\n", (unsigned int)pid1);
    sleep(1);

    pthread_create(&pid2, NULL, run, NULL );
    printf("new thread:%u\n", (unsigned int)pid2);
    sleep(1);

    pthread_create(&pid3, NULL, run, NULL );
    printf("new thread:%u\n", (unsigned int)pid3);
    sleep(1);

    pthread_create(&pid4, NULL, run, NULL );
    printf("new thread:%u\n", (unsigned int)pid4);
    sleep(1);

#if 0
    //不注释
    pthread_mutex_lock(&mutex);
    /*
     new thread:3289088
     线程3289088进入等待状态
     new thread:3825664
     线程3825664进入等待状态
     new thread:4362240
     线程4362240进入等待状态
     new thread:4898816
     线程4898816进入等待状态
     release signal
     release signal
     */
#endif

#if 1
    //注释掉
    //pthread_mutex_lock(&mutex);
    /*
     new thread:3289088
     线程3289088进入等待状态
     new thread:3825664
     线程3825664进入等待状态
     new thread:4362240
     线程4362240进入等待状态
     new thread:4898816
     线程4898816进入等待状态
     release signal
     已经解开3289088
     release signal
     已经解开3825664
     */
#endif

    //pthread_cond_signal每次只会唤醒一个条件锁,唤醒的顺序跟阻塞在条件锁的顺序相同

    i = 2;
    pthread_cond_signal(&cond_l);
    printf("release signal\n");
    sleep(1);

    i = 2;
    pthread_cond_signal(&cond_l);
    printf("release signal\n");
    sleep(1);

    //pthread_join()的作用可以这样理解:主线程等待子线程的终止。
    //也就是在子线程调用了pthread_join()方法后面的代码,只有等到子线程结束了才能执行。
    pthread_join(pid1, NULL );
    pthread_join(pid2, NULL );
    pthread_join(pid3, NULL );
    pthread_join(pid4, NULL );
}