linux C++ 多线程使用pthread_cond 条件变量

简介: 1. 背景多线程中经常需要使用到锁(pthread_mutex_t)来完成多个线程之间的互斥操作。但是互斥锁有一个明显到缺点: 只有两种状态,锁定和非锁定。而条件变量则通过允许线程阻塞并等待另一个线程发送唤醒信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。

1. 背景

多线程中经常需要使用到锁(pthread_mutex_t)来完成多个线程之间的互斥操作。

但是互斥锁有一个明显到缺点: 只有两种状态,锁定和非锁定。

而条件变量则通过允许线程阻塞并等待另一个线程发送唤醒信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。

 

2. 条件变量涉及到的主要函数

2.1 pthread_cond_wait 线程阻塞在条件变量

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

函数将解锁mutex参数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上。
被阻塞的线程可以被pthread_cond_signal函数,pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒。

pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使是函数出错返回
pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值。
最好的测试方法是循环调用pthread_cond_wait函数,并把满足条件的表达式置为循环的终止条件。
1 pthread_mutex_lock();
2 while (condition_is_false)
3     pthread_cond_wait();
4 pthread_mutex_unlock();
阻塞在同一个条件变量上的不同线程被唤醒的次序是不一定的。

 2.2 pthread_cond_signal 线程被唤醒

int pthread_cond_signal(pthread_cond_t *cv);
函数被用来释放被阻塞在指定条件变量上的一个线程。
必须在互斥锁的保护下使用相应的条件变量。否则对条件变量的解锁有可能发生在锁定条件变量之前,从而造成死锁。
唤醒阻塞在条件变量上的所有线程的顺序由调度策略决定,如果线程的调度策略是SCHED_OTHER类型的,系统将根据线程的优先级唤醒线程。
如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal()将没有作用。

更多函数可以看: http://blog.csdn.net/icechenbing/article/details/7662026

 3. 实例代码

实现功能: 2个线程对count每次分别加1, 第三个线程等count大于10后一次加100.

3.1 加1线程函数

 1 void *inc_count(void *idp)  
 2 { 
 3     int i = 0;
 4     int taskid = 0;  
 5     int *my_id = (int*)idp; 
 6 
 7     for (i=0; i<TCOUNT; i++) { 
 8         pthread_mutex_lock(&count_mutex); 
 9         taskid = count;
10         count++; 
11 
12         /*  
13           唤醒一个阻塞在该条件变量到线程
14           如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal()将没有作用
15         */
16         pthread_cond_signal(&count_threshold_cv); 
17 
18         printf("inc_count(): thread %d, count = %d, unlocking mutex\n", *my_id, count); 
19         pthread_mutex_unlock(&count_mutex); 
20         sleep(1);
21     }   
22     printf("inc_count(): thread %d, Threshold reached.\n", *my_id); 
23 
24     pthread_exit(NULL); 
25 } 

3.2 count满足条件后, 单次加100函数

 1 void *watch_count(void *idp)  
 2 { 
 3     int *my_id = (int*)idp; 
 4     printf("Starting watch_count(): thread %d\n", *my_id); 
 5 
 6     pthread_mutex_lock(&count_mutex); 
 7     while(count<COUNT_LIMIT) { 
 8         sleep(3);
 9         /*  
10           函数将自动/原子的解锁count_mutex参数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上
11           被阻塞的线程可以被pthread_cond_signal函数,pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒
12           pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值. 
13           本例子中线程被唤醒后, 仍然在while内会再次判断COUNT_LIMIT是否满足条件的值
14           pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使是函数出错返回
15         */
16         pthread_cond_wait(&count_threshold_cv, &count_mutex); 
17         printf("watch_count(): thread %d Condition signal received.\n", *my_id); 
18     }   
19  
20     count += 100;
21     pthread_mutex_unlock(&count_mutex); 
22     pthread_exit(NULL); 
23 }

3.3 整体代码

 1 #include <pthread.h>
 2 #include <stdio.h>
 3 #include <unistd.h>
 4 
 5 #define NUM_THREADS 3 
 6 #define TCOUNT      10 
 7 #define COUNT_LIMIT 10 
 8 
 9 int count = 0;
10 int thread_ids[3] = {0,1,2};
11 pthread_mutex_t count_mutex;
12 pthread_cond_t  count_threshold_cv;
13 
14 void *inc_count(void *idp)
15 {
16     int i = 0;
17     int taskid = 0;
18     int *my_id = (int*)idp;
19 
20     for (i=0; i<TCOUNT; i++) {
21         pthread_mutex_lock(&count_mutex);
22         taskid = count;
23         count++;
24 
25         /*
26           唤醒一个阻塞在该条件变量到线程
27           如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal()将没有作用
28         */
29         pthread_cond_signal(&count_threshold_cv);
30 
31         printf("inc_count(): thread %d, count = %d, unlocking mutex\n", *my_id, count);
32         pthread_mutex_unlock(&count_mutex);
33         sleep(1);
34     }
35     printf("inc_count(): thread %d, Threshold reached.\n", *my_id);
36 
37     pthread_exit(NULL);
38 }
39 
40 void *watch_count(void *idp)
41 {
42     int *my_id = (int*)idp;
43     printf("Starting watch_count(): thread %d\n", *my_id);
44 
45     pthread_mutex_lock(&count_mutex);
46     while(count<COUNT_LIMIT) {
47         sleep(3);
48         /*
49           函数将自动/原子到解锁mutex参数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上
50           被阻塞的线程可以被pthread_cond_signal函数,pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒
51           pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值. 
52           本例子中使用类COUNT_LIMIT最为满足条件的值
53           pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使是函数出错返回
54         */
55         pthread_cond_wait(&count_threshold_cv, &count_mutex);
56         printf("watch_count(): thread %d Condition signal received.\n", *my_id);
57     }
58 
59     count += 100;
60     pthread_mutex_unlock(&count_mutex);
61     pthread_exit(NULL);
62 }
63 
64 int main (int argc, char *argv[])
65 {
66     int i, rc;
67     pthread_t threads[3];
68     pthread_attr_t attr;
69 
70     /* Initialize mutex and condition variable objects */
71     pthread_mutex_init(&count_mutex, NULL);
72     pthread_cond_init (&count_threshold_cv, NULL);
73 
74     /* For portability, explicitly create threads in a joinable state */
75     pthread_attr_init(&attr);
76     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
77     pthread_create(&threads[0], &attr, inc_count,   (void *)&thread_ids[0]);
78     pthread_create(&threads[1], &attr, inc_count,   (void *)&thread_ids[1]);
79     pthread_create(&threads[2], &attr, watch_count, (void *)&thread_ids[2]);
80 
81     /* Wait for all threads to complete */
82     for (i=0; i<NUM_THREADS; i++) {
83         pthread_join(threads[i], NULL);
84     }
85     printf ("Main(): Waited on %d  threads. Done.\n", NUM_THREADS);
86 
87     /* Clean up and exit */
88     pthread_attr_destroy(&attr);
89     pthread_mutex_destroy(&count_mutex);
90     pthread_cond_destroy(&count_threshold_cv);
91     pthread_exit(NULL);
92 
93     return 0;
94 }
View Code

 

相关文章
|
18天前
|
安全 C++
C++一分钟之-互斥锁与条件变量
【6月更文挑战第26天】在C++并发编程中,`std::mutex`提供互斥访问,防止数据竞争,而`std::condition_variable`用于线程间的同步协调。通过`lock_guard`和`unique_lock`防止忘记解锁,避免死锁。条件变量需配合锁使用,确保在正确条件下唤醒线程,注意虚假唤醒和无条件通知。生产者-消费者模型展示了它们的应用。正确使用这些工具能解决同步问题,提升并发性能和可靠性。
24 4
|
2天前
|
安全 算法 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
11 0
|
2天前
|
存储 安全 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(上)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(上)
9 0
|
11天前
|
Shell Linux
Linux环境变量之shell中export定义全局变量和echo 变量的区别
Linux环境变量之shell中export定义全局变量和echo 变量的区别
|
16天前
|
程序员 编译器 C++
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
14 0
|
18天前
|
域名解析 网络协议 程序员
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
22 0
|
18天前
|
域名解析 网络协议 程序员
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
14 0
|
19天前
|
C++
C++之变量与常量
C++之变量与常量
10 0
|
20天前
|
API C++
c++进阶篇——初窥多线程(三)cpp中的线程类
C++11引入了`std::thread`,提供对并发编程的支持,简化多线程创建并增强可移植性。`std::thread`的构造函数包括默认构造、移动构造及模板构造(支持函数、lambda和对象)。`thread::get_id()`获取线程ID,`join()`确保线程执行完成,`detach()`使线程独立,`joinable()`检查线程状态,`operator=`仅支持移动赋值。`thread::hardware_concurrency()`返回CPU核心数,可用于高效线程分配。
|
2天前
|
缓存 Linux 编译器
【Linux】多线程——线程概念|进程VS线程|线程控制(下)
【Linux】多线程——线程概念|进程VS线程|线程控制(下)
7 0