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

 

相关文章
|
2月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
91 1
Linux C/C++之IO多路复用(aio)
|
28天前
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
38 7
|
28天前
|
消息中间件 存储 安全
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
48 1
C++ 多线程之初识多线程
|
2月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
2月前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
347 3
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
53 6
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
25 0
C++ 多线程之线程管理函数
|
2月前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
31 0
Linux C/C++之线程基础
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3