嵌入式 pthread_cond_broadcast相关

来源:互联网 发布:linux安装时磁盘分区 编辑:程序博客网 时间:2024/06/03 21:28
pthread_cond_timedwait()函数阻塞住调用该函数的线程,等待由cond指定的条件被触发(pthread_cond_broadcast()or pthread_cond_signal())。

当pthread_cond_timedwait()被调用时,调用线程必须已经锁住了mutex。函数pthread_cond_timedwait()会对mutex进行【解锁和执行对条件的等待】(原子操作)。这里的原子意味着:解锁和执行条件的等待是原则的,一体的。(Inthis case, atomically means with respect to the mutex and thecondition variable and other access by threads to those objectsthrough the pthread condition variable interfaces.)

如果等待条件满足或超时,或线程被取消,调用线程需要在线程继续执行前先自动锁住mutex,如果没有锁住mutex,产生EPERM错误。即,该函数返回时,mutex已经被调用线程锁住。

等待的时间通过abstime参数(绝对系统时间,过了该时刻就超时)指定,超时则返回ETIMEDOUT错误码。开始等待后,等待时间不受系统时钟改变的影响。

尽管时间通过秒和纳秒指定,系统时间是毫秒粒度的。需要根据调度和优先级原因,设置的时间长度应该比预想的时间要多或者少点。可以通过使用系统时钟接口gettimeofday()获得timeval结构体。


多线程编程中,线程A循环计算,然后sleep一会接着计算(目的是减少CPU利用率);存在的问题是,如果要关闭程序,通常选择join线程A等待线程A退出,可是我们必须等到sleep函数返回,该线程A才能正常退出,这无疑减慢了程序退出的速度。当然,你可以terminate线程A,但这样做很不优雅,且会存在一些未知问题。采用pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t *mutex, const struct timespec *abstime)可以优雅的解决该问题,设置等待条件变量cond,如果超时,则返回;如果等待到条件变量cond,也返回。本文暂不将内部机理,仅演示一个demo

 

[cpp] viewplaincopyprint?
  1. staticunsignedcharflag =1;
  2. void *thr_fn(void*arg)
  3. {
  4. while(flag)
  5. {
  6. printf(“threadsleep 10second\n”);
  7. sleep(10);
  8. }
  9. printf(“threadexit\n”);
  10. }
  11. intmain()
  12. {
  13. pthread_t thread;
  14. if (0 !=pthread_create(&thread, NULL, thr_fn,NULL))
  15. {
  16. printf(“error when create pthread,%d\n”,errno);
  17. return1;
  18. }
  19. char c ;
  20. while ((c = getchar()) !=‘q’);
  21. printf(“Now terminate the thread!\n”);
  22. flag = 0;
  23. printf(“Wait forthread toexit\n”);
  24. pthread_join(thread,NULL);
  25. printf(“Bye\n”);
  26. return0;
  27. }
static unsigned char flag = 1;void * thr_fn(void * arg){        while (flag)        {                printf(“thread sleep 10 second\n”);                sleep(10);        }        printf(“thread exit\n”);}int main(){        pthread_t thread;        if (0 != pthread_create(&thread, NULL, thr_fn, NULL))        {                printf(“error when create pthread,%d\n”, errno);                return 1;        }        char c ;        while ((c = getchar()) != ‘q’);        printf(“Now terminate the thread!\n”);        flag = 0;        printf(“Wait for thread to exit\n”);        pthread_join(thread, NULL);        printf(“Bye\n”);        return 0;}

输入q后,需要等线程从sleep中醒来(由挂起状态变为运行状态),即最坏情况要等10s,线程才会被join。采用sleep的缺点:不能及时唤醒线程。
采用pthread_cond_timedwait函数实现的如下

 

[cpp] viewplaincopyprint?
  1. include
  2. include
  3. include
  4. include
  5. include
  6. pthread_t thread;
  7. pthread_cond_t cond;
  8. pthread_mutex_t mutex;
  9. unsigned charflag =1;
  10. void *thr_fn(void*arg)
  11. {
  12. structtimevalnow;
  13. structtimespecouttime;
  14. pthread_mutex_lock(&mutex);
  15. while(flag)
  16. {
  17. printf(“threadsleepnow\n”);
  18. gettimeofday(&now, NULL);
  19. outtime.tv_sec = now.tv_sec + 10;
  20. outtime.tv_nsec = now.tv_usec * 1000;
  21. pthread_cond_timedwait(&cond, &mutex,&outtime);
  22. }
  23. pthread_mutex_unlock(&mutex);
  24. printf(“threadexit\n”);
  25. }
  26. intmain()
  27. {
  28. char c ;
  29. pthread_mutex_init(&mutex, NULL);
  30. pthread_cond_init(&cond,NULL);
  31. if (0 !=pthread_create(&thread, NULL, thr_fn,NULL))
  32. {
  33. printf(“error when create pthread,%d\n”,errno);
  34. return1;
  35. }
  36. while ((c = getchar()) !=‘q’);
  37. printf(“Now terminate the thread!\n”);
  38. flag = 0;
  39. pthread_mutex_lock(&mutex);
  40. pthread_cond_signal(&cond);
  41. pthread_mutex_unlock(&mutex);
  42. printf(“Wait forthread toexit\n”);
  43. pthread_join(thread,NULL);
  44. printf(“Bye\n”);
  45. return0;
  46. }
include include include include include pthread_t thread;pthread_cond_t cond;pthread_mutex_t mutex;unsigned char flag = 1;void * thr_fn(void * arg){        struct timeval now;        struct timespec outtime;        pthread_mutex_lock(&mutex);        while (flag)        {                printf(“thread sleep now\n”);                gettimeofday(&now, NULL);                outtime.tv_sec = now.tv_sec + 10;                outtime.tv_nsec = now.tv_usec * 1000;                pthread_cond_timedwait(&cond, &mutex, &outtime);        }        pthread_mutex_unlock(&mutex);        printf(“thread exit\n”);}int main(){        char c ;        pthread_mutex_init(&mutex, NULL);        pthread_cond_init(&cond, NULL);        if (0 != pthread_create(&thread, NULL, thr_fn, NULL))        {                printf(“error when create pthread,%d\n”, errno);                return 1;        }                while ((c = getchar()) != ‘q’);        printf(“Now terminate the thread!\n”);        flag = 0;        pthread_mutex_lock(&mutex);        pthread_cond_signal(&cond);        pthread_mutex_unlock(&mutex);        printf(“Wait for thread to exit\n”);        pthread_join(thread, NULL);        printf(“Bye\n”);        return 0;}

条件变量是 GNU/Linux提供的第三种同步工具(第一互斥体第二这信号量);利用它你可以在多线程环境下实现更复杂的条件控制。

当标志没有被设置的时候,线程会不断循环检测这个标志同时会不断锁定、解锁互斥体,浪费CPU时间。你真正需要的是这样一种方法:当标志没有设置的时候让线程进入休眠状态;而当某种特定条件出现时,标志位被设置,线程被唤醒。

如同信号量,线程可以对一个条件变量执行等待操作。如果如果线程 A正在等待一个条件变量,它会被阻塞直到另外一个线程B,向同一个条件变量发送信号以改变其状态。不同于信号量,条件变量没有计数值,也不占据内存空间,线程A 必须在 B 发送信号之前开始等待。如果 B 在 A 执行等待操作之前发送了信号,这个信号就丢失了,同时A会一直阻塞直到其它线程再次发送信号到这个条件变量。

条件变量将允许你实现这样的目的:在一种情况下令线程继续运行,而相反情况下令线程阻塞。只要每个可能涉及到改变状态的线程正确使用条件变量,Linux将保证当条件改变的时候由于一个条件变量的状态被阻塞的线程均能够被激活。

GNU/Linux 刚好提供了这个机制,每个条件变量都必须与一个互斥体共同使用,以防止这种竞争状态的发生。这种设计下,线程函数应遵循以下步骤:

  1. thread_function中的循环首先锁定互斥体并且读取标志变量的值。
  2. 如果标志变量已经被设定,该线程将互斥体解锁然后执行工作函数
  3. 如果标志没有被设置,该线程自动锁定互斥体并开始等待条件变量的信号

这里最关键的特点就在第三条。这里,GNU/Linux系统允许你用一个原子操作完成解除互斥体锁定和等待条件变量信号的过程而不会被其它线程在中途插入执行。这就避免了在thread_function中检测标志和等待条件变量的过程中其它线程修改标志变量并对条件变量发送信号的可能性。

pthread_cond_t pCond;

pthread_cond_init(&pCond,NULL);第一个参数是一个指向pthread_cond_t变量的指针。第二个参数是一个指向条件变量属性对象的指针;这个参数在GNU/Linux 系统中是被忽略的。

pthread_cond_signal(&pCond)如果没有线程正在等待这个信号,则这个信号会被忽略。该函数的
参数是一个指向 pthread_cond_t 类型变量的指针。
pthread_cond_broadcast()函数会将所有等待该条件变量的线程解锁而不是仅仅解锁一个线程

pthread_cond_wait(&pCond,&mutex)会让调用线程阻塞直到条件变量收到信号。第一个参数是指向一个pthread_cond_t类型变量的指针,第二个参数是指向一个pthread_mutex_t类型变量的指针。当调用 pthread_cond_wait的时候,互斥体对象必须已经被调用线程锁定。这个函数以一个原子操作解锁互斥体并锁定条件变量等待信号。当信号到达且调用线程被解锁之后,pthread_cond_wait自动申请锁定互斥体对象。


pthread_mutex_lock(&qlock);
pthread_cond_wait(&qready, &qlock);//其它线程pthread_cond_sigal发出信号才会对出阻塞
pthread_mutex_unlock(&qlock);

The mutex passed to pthread_cond_wait protects the condition.Thecaller passes it locked to the function, which thenatomicallyplaces the calling thread on the list of threads waiting fortheconditionand unlocks the mutex. This closes the window between thetime that thecondition is checked and the time that thethread goes to sleep waiting for the condition to change, so thatthe thread doesn't miss a change in the condition. Whenpthread_cond_wait returns, the mutex is again locked.
上面是APUE的原话,就是说pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t*mutex)

函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,如果条件不成立我们就进入阻塞,但是进入阻塞这个期间,如果条件变量改变了的话,那我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁,使得其它线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又自动给mutex加锁
实际上边代码的加解锁过程如下:

pthread_mutex_lock(&qlock);
pthread_cond_wait(&qready, &qlock);
pthread_mutex_unlock(&qlock);


最近看《UNIX环境高级编程》多线程同步,看到他举例说条件变量pthread_cond_t怎么用,愣是没有看懂,只好在网上找了份代码,跑了跑,才弄明白

[cpp] viewplaincopyprint?
  1. #include
  2. #include
  3. #include
  4. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  5. pthread_cond_t cond =PTHREAD_COND_INITIALIZER;
  6. void *thread1(void*);
  7. void *thread2(void*);
  8. int i=1;
  9. int main(void)
  10. {
  11. pthread_t t_a;
  12. pthread_t t_b;
  13. pthread_create(&t_a,NULL,thread1,(void*)NULL);
  14. pthread_create(&t_b,NULL,thread2,(void*)NULL);
  15. pthread_join(t_a, NULL);
  16. pthread_join(t_b, NULL);
  17. pthread_mutex_destroy(&mutex);
  18. pthread_cond_destroy(&cond);
  19. exit(0);
  20. }
  21. void *thread1(void*junk)
  22. {
  23. for(i=1;i<=6;i++)
  24. {
  25. pthread_mutex_lock(&mutex);
  26. printf("thread1:lock %d/n", __LINE__);
  27. if(i%3==0){
  28. printf("thread1:signal 1 %d/n", __LINE__);
  29. pthread_cond_signal(&cond);
  30. printf("thread1:signal 2 %d/n", __LINE__);
  31. sleep(1);
  32. }
  33. pthread_mutex_unlock(&mutex);
  34. printf("thread1:unlock %d/n/n", __LINE__);
  35. sleep(1);
  36. }
  37. }
  38. void *thread2(void*junk)
  39. {
  40. while(i<6)
  41. {
  42. pthread_mutex_lock(&mutex);
  43. printf("thread2: lock %d/n", __LINE__);
  44. if(i%3!=0){
  45. printf("thread2: wait 1 %d/n", __LINE__);
  46. pthread_cond_wait(&cond,&mutex);
  47. printf("thread2: wait 2 %d/n", __LINE__);
  48. }
  49. pthread_mutex_unlock(&mutex);
  50. printf("thread2:unlock %d/n/n", __LINE__);
  51. sleep(1);
  52. }
  53. }
[cpp] viewplaincopyprint?
  1. #include
  2. #include
  3. #include
  4. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  5. pthread_cond_t cond =PTHREAD_COND_INITIALIZER;
  6. void*thread1(void*);
  7. void*thread2(void*);
  8. int i=1;
  9. int main(void)
  10. {
  11. pthread_t t_a;
  12. pthread_t t_b;
  13. pthread_create(&t_a,NULL,thread1,(void*)NULL);
  14. pthread_create(&t_b,NULL,thread2,(void*)NULL);
  15. pthread_join(t_a, NULL);
  16. pthread_join(t_b, NULL);
  17. pthread_mutex_destroy(&mutex);
  18. pthread_cond_destroy(&cond);
  19. exit(0);
  20. }
  21. void*thread1(void*junk)
  22. {
  23. for(i=1;i<=6;i++)
  24. {
  25. pthread_mutex_lock(&mutex);
  26. printf("thread1: lock %d/n",__LINE__);
  27. if(i%3==0){
  28. printf("thread1:signal 1 %d/n",__LINE__);
  29. pthread_cond_signal(&cond);
  30. printf("thread1:signal 2 %d/n",__LINE__);
  31. sleep(1);
  32. }
  33. pthread_mutex_unlock(&mutex);
  34. printf("thread1: unlock %d/n/n",__LINE__);
  35. sleep(1);
  36. }
  37. }
  38. void*thread2(void*junk)
  39. {
  40. while(i<6)
  41. {
  42. pthread_mutex_lock(&mutex);
  43. printf("thread2: lock %d/n",__LINE__);
  44. if(i%3!=0){
  45. printf("thread2: wait 1 %d/n",__LINE__);
  46. pthread_cond_wait(&cond,&mutex);
  47. printf("thread2: wait 2 %d/n",__LINE__);
  48. }
  49. pthread_mutex_unlock(&mutex);
  50. printf("thread2: unlock %d/n/n",__LINE__);
  51. sleep(1);
  52. }
  53. }
#include #include 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,thread1,(void *)NULL);pthread_create(&t_b,NULL,thread2,(void *)NULL);pthread_join(t_a, NULL); pthread_join(t_b, NULL);pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond);exit(0); } void *thread1(void *junk) { for(i=1;i<=6;i++) {pthread_mutex_lock(&mutex); printf("thread1: lock %d/n",__LINE__); if(i%3==0){ printf("thread1:signal 1 %d/n", __LINE__);pthread_cond_signal(&cond); printf("thread1:signal 2 %d/n",__LINE__); sleep(1); } pthread_mutex_unlock(&mutex);printf("thread1: unlock %d/n/n", __LINE__); sleep(1); } } void*thread2(void *junk) { while(i<6) {pthread_mutex_lock(&mutex); printf("thread2: lock %d/n",__LINE__); if(i%3!=0){ printf("thread2: wait 1 %d/n", __LINE__);pthread_cond_wait(&cond,&mutex); printf("thread2: wait 2%d/n", __LINE__); } pthread_mutex_unlock(&mutex);printf("thread2: unlock %d/n/n", __LINE__); sleep(1); } }

编译:

[X61@horizon threads]$ gcc thread_cond.c -lpthread -o tcd

以下是程序运行结果:

[X61@horizon threads]$ ./tcd
thread1: lock 30
thread1: unlock 40

thread2: lock 52
thread2: wait 1 55
thread1: lock 30
thread1: unlock 40

thread1: lock 30
thread1:signal 1 33
thread1:signal 2 35
thread1: unlock 40

thread2: wait 2 57
thread2: unlock 61

thread1: lock 30
thread1: unlock 40

thread2: lock 52
thread2: wait 1 55
thread1: lock 30
thread1: unlock 40

thread1: lock 30
thread1:signal 1 33
thread1:signal 2 35
thread1: unlock 40

thread2: wait 2 57
thread2: unlock 61

这里的两个关键函数就在pthread_cond_wait和pthread_cond_signal函数。

本例中:

线程一先执行,获得mutex锁,打印,然后释放mutex锁,然后阻塞自己1秒。

线程二此时和线程一应该是并发的执行
,这里是一个要点,为什么说是线程此时是并发的执行,因为此时不做任何干涉的话,是没有办法确定是线程一先获得执行还是线程二先获得执行,到底那个线程先获得执行,取决于操作系统的调度,想刻意的让线程2先执行,可以让线程2一出来,先sleep一秒。
这里并发执行的情况是,线程一先进入循环,然后获得锁,此时估计线程二执行,阻塞在
pthread_mutex_lock(&mutex);
这行语句中,直到线程1释放mutex锁
pthread_mutex_unlock(&mutex);
然后线程二得已执行,获取metux锁,满足if条件,到pthread_cond_wait(&cond,&mutex);
这里的线程二阻塞,不仅仅是等待cond变量发生改变,同时释放mutex锁,因为当时看书没有注意,所以这里卡了很久。
mutex锁释放后,线程1终于获得了mutex锁,得已继续运行,当线程1的if(i%3==0)的条件满足后,通过pthread_cond_signal发送信号,告诉等待cond的变量的线程(这个情景中是线程二),cond条件变量已经发生了改变。

不过此时线程二并没有立即得到运行,因为线程二还在等待mutex锁的释放,所以线程一继续往下走,直到线程一释放mutex锁,线程二才能停止等待,打印语句,然后往下走通过pthread_mutex_unlock(&mutex)释放mutex锁,进入下一个循环。

原创粉丝点击