线程同步----条件变量,pthread_cond_wait()与pthread_cond_signal()到底干了什么

时间紧可以跳过前面的介绍,后面的实例代码详解才是重点。

一、什么是条件变量

       与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

       条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

条 件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件 变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

       使用条件变量之前要先进行初始化。可以在单个语句中生成和初始化一个条件变量如:

pthread_cond_t my_condition=PTHREAD_COND_INITIALIZER;(用于进程间线程的通信)。

也可以利用函数pthread_cond_init动态初始化。

二、条件变量函数

1.

名称:

pthread_cond_init

目标:

条件变量初始化

头文件:

#include < pthread.h>

函数原形:

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

参数:

cond  条件变量

attr  条件变量属性

返回值:

成功返回0,出错返回错误编号。

     

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

2.

名称:

pthread_cond_destroy

目标:

条件变量摧毁

头文件:

#include < pthread.h>

函数原形:

int pthread_cond_destroy(pthread_cond_t *cond);

参数:

cond  条件变量

返回值:

成功返回0,出错返回错误编号。

      

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

3.

名称:

pthread_cond_wait/pthread_cond_timedwait

目标:

条件变量等待

头文件:

#include < pthread.h>

函数原形:

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

int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t mytex,const struct timespec *abstime);

参数:

cond 条件变量

mutex 互斥锁

返回值:

成功返回0,出错返回错误编号。

      

第一个参数*cond是指向一个条件变量的指针。第二个参数*mutex则是对相关的互斥锁的指针。函数pthread_cond_timedwait函数类型与函数pthread_cond_wait,区别在于,如果达到或是超过所引用的参数*abstime,它将结束并返回错误ETIME.pthread_cond_timedwait函数的参数*abstime指向一个timespec结构。该结构如下:

typedef struct timespec{

       time_t tv_sec;

       long tv_nsex;

}timespec_t;

4.

名称:

pthread_cond_signal/pthread_cond_broadcast

目标:

条件变量通知

头文件:

#include < pthread.h>

函数原形:

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

参数:

cond 条件变量

返回值:

成功返回0,出错返回错误编号。

      

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

下面是一个简单的例子,我们可以从程序的运行来了解条件变量的作用。

/*
 *程序创建了2个新线程使他们同步运行,实现进程t_b打印10以内3的倍数,t_a打印其他的数,程序开始线程t_b不满足条件等待,
 * 线程t_a运行使a循环加1并打印。直到i为3的倍数时,线程t_a发送信号通知进程t_b,这时t_b满足条件,打印i值。
 *********************************************************************************************************
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*静态初始化互斥锁*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/
void *thread1(void *);
void *thread2(void *);
int i=1;


int main(void)
{
    pthread_t t_a;
    pthread_t t_b;
    pthread_create(&t_a,NULL,thread2,(void *)NULL);/*创建进程t_a*/
    pthread_create(&t_b,NULL,thread1,(void *)NULL); /*创建进程t_b*/
    pthread_join(t_b, NULL);/*等待进程t_b结束*/
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    exit(0);
}


void *thread1(void *junk)
{
    for(i=1;i<=9;i++)
    {
        pthread_mutex_lock(&mutex);/*锁住互斥量*/
        if(i%3==0)
        {
             pthread_cond_signal(&cond);/*条件改变,发送信号,通知t_b进程*/
             sleep(1);
             printf("已唤醒thread2\n");
        }
        else
        {
             printf("thread1:%d\n",i);
        }
        pthread_mutex_unlock(&mutex);/*解锁互斥量*/
        printf("thread1 unlock\n");
        sleep(1);
    }


}


void *thread2(void *junk)
{
    while(i<9)
    {
        pthread_mutex_lock(&mutex);
        sleep(1);
        printf("thread2 get lock\n");
        if(i%3!=0)
        {
        printf("开始开始等待\n");
        pthread_cond_wait(&cond,&mutex);/*等待*/
        printf("结束等待\n");
        printf("thread2:%d\n",i);
        }
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}

下面是运行结果:

#cc –lpthread –o cond cond.c

#./cond

1,thread2 get lock         thread2获得锁

2,开始开始等待               pthread_cond_wait解锁,再阻塞

3,thread1:1                    thread1获得锁

4,thread1 unlock           thread1释放锁

5,thread1:2                    thread2阻塞中不会竞争锁,thread1再次获得锁

6,thread1 unlock           thread1释放锁

7,已唤醒thread2            此时i=3,唤醒thread2,thread2进入就绪状态

8,thread1 unlock           thread1释放锁

9,结束等待                      thread2获得锁退出阻塞,执行pthread_cond_wait后的代码

10,thread2:3                    打印这句后thread2解锁

11,thread2 get lock         thread2获得锁, <为什么此时不是thread1获得锁,为了让thread1获得锁,我在thread2解锁后还sleep(1)了,但还是 thread2获得锁>

12,开始开始等待               pthread_cond_wait解锁,再阻塞

13,thread1:4                    。。。

14,thread1 unlock           。。。

15,thread1:5

16,thread1 unlock

17,已唤醒thread2

18,thread1 unlock

19,结束等待

20,thread2:6

21,thread1:7

22,thread1 unlock

23,thread2 get lock

24,开始开始等待

25,thread1:8

26,thread1 unlock

27,已唤醒thread2

28,thread1 unlock

29,结束等待

30,thread2:9

1~30的标号不是打印信息哈

结论:

pthread_cond_wait() 必须与pthread_mutex_lock() 配套使用。pthread_cond_wait()函数一进入wait状态就会自动解锁(有原子操作,先解锁,再阻塞)。当其他线程(线程乙)通过pthread_cond_signal()或pthread_cond_broadcast,把该线程(线程甲)唤醒,使pthread_cond_wait()进入就绪状态

1,如果此时发信号pthread_cond_signal()的线程乙已经释放锁,线程甲自动获得该mutex,继续执行pthread_cond_wait()后的代码。

2,如果此时发信号pthread_cond_signal()的线程乙还没有释放锁,则线程甲继续保持就绪状态直到线程乙释放锁后,线程甲再获得mutex

程序详解参考如下链接

https://weihe6666.iteye.com/blog/1170141

但这个链接说

pthread_cond_signal(&cond)有原子操作(先解锁,再发送信号)????

问题:

①,上述结论是我根据执行结果做的猜想,真实情况是否是我猜想的这样?

②,我用上述的方法是否可以证明pthread_cond_signal(&cond)没有原子操作,并不会解锁,只是发信号????

③,执行结果的11步为什么不是thread1获得锁?


三天后找到答案:
我的操作系统:CentOS7

①,

根据UNIX网络编程(卷2)第七章得:上述猜想基本正确,pthread_cond_wait()函数确定有原子操作,pthread_cond_signal(&cond)没有原子操作,于是为了避免上锁冲突,可以在当前线程里先解锁,再调用pthread_cond_signal( )函数发送信号。

书中介绍了一种上锁冲突的情况:

先调pthread_cond_signal(&cond )再释放锁,当该条件变量被发送信号后,系统立即调度正在等待中的线程,该线程开始运行,但立即停止,因为它没能获取相应的互斥锁。

②,

执行结果的11步为什么不是thread1获得锁,为了让thread1获得锁,我在thread2解锁后还sleep(1)了

根据UNIX网络编程(卷2)第七章得:在多线程的程序中,主程序里在创建多线程前应该调用pthread_setconcurrency(nthreads)函数,保证生成的线程都有执行机会,该函数输入参数为子线程个数,我在本例代码的主线程添加这个函数后,第11步就变成了thread1先获得锁。

同时对于多线程编程,主线程在创建线程后应该调用pthread_join( )函数,等待子线程执行完后再结束主线程。

③,

根据书上7.3节生产者-消费者代码例程,我对其编译运行测试后,发现当A为13万时,线程一和线程二各执行了一部分次数,当A小于13万时,线程一执行A次,线程二不执行,得出一个新猜想:for循环与while循环的切换速度,是不同线程间竞争获取到锁这个速度的13万倍左右。

主要代码逻辑
主程序逻辑:设置一个总的for循环次数A,如果线程一执行了A次,则线程二不执行,最终使得两线程总共执行for循环次数相加为A

线程一:
for循环/while循环
{
pthread_mutex_lock(&mutex);

....

pthread_mutex_unlock(&mutex);
}


线程二:
for循环/while循环
{
pthread_mutex_lock(&mutex);

....

pthread_mutex_unlock(&mutex);
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pthread_cond_broadcast和pthread_cond_wait也是用于线程间同步的函数,类似于pthread_cond_signal和pthread_cond_wait的组合,但有一些区别。 pthread_cond_broadcast用于广播条件变量的信号。当一个线程调用pthread_cond_broadcast时,它会唤醒所有正在等待这个条件变量的线程。这与pthread_cond_signal的区别在于,pthread_cond_signal只会唤醒一个等待线程,而pthread_cond_broadcast会唤醒所有等待线程。 pthread_cond_wait用于等待条件变量的信号,与pthread_cond_signal和pthread_cond_broadcast一起使用。当一个线程调用pthread_cond_wait时,它会阻塞等待条件变量的信号。当收到信号后,线程会重新激活,并且会重新检查条件是否满足。如果条件不满足,线程可能会再次进入等待状态。 以下是一个使用pthread_cond_broadcast和pthread_cond_wait的示例代码: ```c pthread_mutex_t mutex; pthread_cond_t cond; int condition = 0; void* thread1(void* arg) { pthread_mutex_lock(&mutex); while (condition == 0) { pthread_cond_wait(&cond, &mutex); } // 条件满足后执行的代码 pthread_mutex_unlock(&mutex); return NULL; } void* thread2(void* arg) { pthread_mutex_lock(&mutex); condition = 1; pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t tid1, tid2; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); pthread_create(&tid1, NULL, thread1, NULL); pthread_create(&tid2, NULL, thread2, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; } ``` 在上述示例中,thread1线程调用pthread_cond_wait等待条件满足,而thread2线程在某个时刻将条件设置为满足,并调用pthread_cond_broadcast发送信号。这样,所有等待的线程都会被唤醒并执行相应的代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值