pThread线程(三) 线程同步--条件变量

条件变量(Condition Variables)

参考资料:http://game-lab.org/posts/posix-thread-cn/#5.1

条件变量是什么?

  • 条件变量为我们提供了另一种线程间同步的方法,然而,互斥量是通过控制线程访问数据来实现同步,条件变量允许线程同步是基于实际数据的值。
  • 如果没有条件变量,程序员需要让线程不断地轮询,以检查是否满足条件。由于线程处在一个不间断的忙碌状态,所以这是相当耗资源的。条件变量就是这么一个不需要轮询就可以解决这个问题的方法。
  • 条件变量总是跟互斥锁(mutex lock)一起使用。
  • 下面是使用条件变量的比较典型的过程:
主线程

  • 声明并初始化需要同步的全局数据或变量(例如”count“)
  • 声明并初始化一个条件变量对象
  • 声明并初始化一个与条件变量关联的互斥量
  • 创建线程A和B并开始运行
线程A

  • 线程运转至某一个条件被触发(例如,”count“必须达到某个值)
  • 锁定相关联的互斥量并检查全局变量的值
  • 调用pthread_con_wait()阻塞线程等待线程B的信号。请注意,调用pthread_con_wait()以自动的原子方式(atomically)解锁相关联的互斥量,以便于可以被线程B使用。
  • 当收到信号时,唤醒线程。互斥量被以自动的原子方式被锁定。
  • 明确的解锁互斥量。
  • 继续
Thread B

  • 线程运转
  • 锁定相关联的互斥量
  • 更改线程A正在等待的全局变量的值
  • 检查线程A等待的变量值,如果满足条件,发信号给线程A
  • 解锁互斥量
  • 继续
主线程

    Join / Continue

创建和销毁条件变量

函数:

pthread_cond_init (condition,attr)
pthread_cond_destroy (condition)

pthread_condattr_init (attr)
pthread_condattr_destroy (attr)

用法:

  • 条件变量必须声明为pthread_cond_t类型,并且在使用前必须要初始化。初始化,有两种方法:
  1. 静态初始化,像这样声明:pthread_con_t myconvar = PTHREAD_CON_INITIALIZER;
  2. 动态初始化,使用pthread_cond_init()函数。用创建条件变量的ID作为件参数传给线程,这种方法允许设置条件变量对象属性attr。
  • 可设置的attr对象经常用来设置条件变量的属性,条件变量只有一种属性:process-thread,它的作用是允许条件变量被其它进程的线程看到。如果使用属性对象,必须是pthread_condattr_t类型(也可以赋值为NULL,作为默认值)。

注意,不是所有的实现都用得着process-shared属性。

  • pthread_condattr_init()和pthread_condattr_destroy()函数是用来创建和销毁条件变量属性对象的。
  • 当不再需要某条件变量时,可用pthread_cond_destroy()销毁。

条件变量的等待和信号发送

函数:

pthread_cond_wait (condition,mutex)
pthread_cond_signal (condition)
pthread_cond_broadcast (condition)

使用:

  • pthread_cond_wait()阻塞调用线程,直到指定的条件变量收到信号。当互斥量被锁定时,应该调用这个函数,并且在等待时自动释放这个互斥量,在接收到信号后线程被唤醒,线程的互斥量会被自动锁定,程序员在线程中应当在此函数后解锁互斥量。
  • pthread_cond_signal()函数常用来发信号给(或唤醒)正在等待条件变量的另一个线程,在互斥量被锁定后应该调用这个函数,并且为了pthread_cond_wait()函数的完成必须要解锁互斥量。
  • 如果多个线程处于阻塞等待状态,那么必须要使用pthreads_cond_broadcast()函数,而不是pthread_cond_signal()。
  • 在调用pthread_cond_wait()函数之前调用pthread_cond_signal()函数是个逻辑上的错误,所以,在使用这些函数时,正确的锁定和解锁与条件变量相关的互斥量是非常必要的,例如:
  1. 在调用pthread_cond_wait()之前锁定互斥量失败,可致使其无法阻塞;
  2. 在调用pthread_cond_signal()之后解锁互斥量失败,则致使与之对应的pthread_cond_wait()函数无法完成,并仍保持阻塞状态。

实例分析

 1 /******************************************************************************
 2  * 描述:
 3  *     应用Pthreads条件变量的实例代码,主线程创建三个线程,其中两个为“count”变量做
 4  * 加法运算,第三个线程监视“count”的值。当“count”达到一个限定值,等待线程准备接收来
 5  * 自于两个加法线程中一个的信号,等待 线程唤醒后更改“count”的值。程序继续运行直到加法
 6  * 线程达到TCOUNT的值。最后,主程序打印出count的值。
 7  ******************************************************************************/
 8 #include <pthread.h>
 9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #define NUM_THREADS  3
13 #define TCOUNT 5       //单线程轮询次数
14 #define COUNT_LIMIT 7  //发送信号的次数
15 int count = 0; //全局的累加量
16
17 pthread_mutex_t count_mutex;
18 pthread_cond_t count_threshold_cv;
19
20 void *inc_count(void *t) {
21     int i;
22     long my_id = (long) t;
23
24     for (i = 0; i < TCOUNT; i++) {
25         pthread_mutex_lock(&count_mutex);
26         count++;
27         /*
28          * 检查count的值,如果条件满足就发信号给等待线程
29          * 注意,此处是用信号量锁定的。
30          * */
31         if (count < COUNT_LIMIT) {
32             printf("inc_count(): thread %ld, count = %d  Threshold reached. ",
33                     my_id, count);
34             pthread_cond_signal(&count_threshold_cv);
35             printf("Just sent signal.\n");
36         }
37         printf("inc_count(): thread %ld, count = %d, unlocking mutex\n", my_id,
38                 count);
39         pthread_mutex_unlock(&count_mutex);
40
41         /*为线程轮询互斥锁增加延时*/
42         sleep(1);
43     }
44     pthread_exit(NULL);
45 }
46
47 void *watch_count(void *t) {
48     long my_id = (long) t;
49     printf("Starting watch_count(): thread %ld\n", my_id);
50
51     /*锁定互斥量并等待信号,注意,pthread_cond_wait函数在等待时将自动以自动原子方式
52      * 解锁互斥量。还有,请注意,如果等待线程运行到等待函数之前已经满足COUNT_LIMIT的
53      * 条件判断,轮询会忽略掉等待函数,
54      * */
55     while (count < COUNT_LIMIT) {
56         pthread_mutex_lock(&count_mutex);
57         printf("watch_count(): thread %ld going into wait...\n", my_id);
58         pthread_cond_wait(&count_threshold_cv, &count_mutex);
59         printf("watch_count(): thread %ld Condition signal received.\n", my_id);
60
61         printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
62         pthread_mutex_unlock(&count_mutex);
63     }
64     pthread_exit(NULL);
65 }
66
67 int main(int argc, char *argv[]) {
68     int i;
69     long t1 = 1, t2 = 2, t3 = 3;
70     pthread_t threads[3];
71     pthread_attr_t attr;
72
73     /*初始化互斥量和条件变量对象*/
74     pthread_mutex_init(&count_mutex, NULL);
75     pthread_cond_init(&count_threshold_cv, NULL);
76
77     /*创建线程时设为可连接状态,便于移植*/
78     pthread_attr_init(&attr);
79     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
80     pthread_create(&threads[0], &attr, watch_count, (void *) t1);
81     pthread_create(&threads[1], &attr, inc_count, (void *) t2);
82     pthread_create(&threads[2], &attr, inc_count, (void *) t3);
83
84     /* 等待所有线程完成*/
85     for (i = 1; i < NUM_THREADS; i++) {
86         pthread_join(threads[i], NULL);
87     }
88    /*发送信号给监听线程*/
89     pthread_cond_signal(&count_threshold_cv);
90     pthread_join(threads[0],NULL);
91     printf("Main(): Waited on %d threads. Final value of count = %d. Done.\n",
92             NUM_THREADS, count);
93
94     /*清除并退出 */
95     pthread_attr_destroy(&attr);
96     pthread_mutex_destroy(&count_mutex);
97     pthread_cond_destroy(&count_threshold_cv);
98     pthread_exit(NULL);
99 }

 

时间: 2024-10-11 23:21:41

pThread线程(三) 线程同步--条件变量的相关文章

linux系统编程:线程同步-条件变量(cond)

线程同步-条件变量(cond) 生产者与消费者问题 再引入条件变量之前,我们先看下生产者和消费者问题:生产者不断地生产产品,同时消费者不断地在消费产品. 这个问题的同步在于两处:第一,消费者之间需要同步:同一件产品只可由一人消费.第二,当无产品可消费时,消费者需等待生产者生产后,才可继续消费,这又是一个同步问题.详细了解:生产者消费者问题. 条件变量 条件变量是利用线程间共享的全局变量进行同步的一种机制,并且条件变量总是和互斥锁结合在一起. 相关函数 pthread_cond_t //条件变量类

线程同步——条件变量

1.互斥量的存在问题:     互斥量是线程程序必需的工具,但它们并非万能的.例如,如果线程正在等待共享数据内某个条件出现,那会发生什么呢?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结构,以查找某个值.但这是在浪费时间和资源,而且这种繁忙查询的效率非常低. 在每次检查之间,可以让调用线程短暂地进入睡眠,比如睡眠三秒钟,但是因此线程代码就无法最快作出响应.真正需要的是这样一种方法:当线程在等待满足某些条件时使线程进入睡眠状态.一旦条件满足,就唤醒因等待满足特定条件而睡眠的线程.如果能够做

linux线程间通信之条件变量和互斥量

一.条件变量定义 有的时候仅仅依靠锁住共享资源来使用它是不够的.有时候共享资源只有某些状态的时候才能够使用.比方说,某个线程如果要从堆栈中读取数据,那么如果栈中没有数据就必须等待数据被压栈.这种情况下的同步使用互斥锁是不够的.另一种同步的方式--条件变量,就可以使用在这种情况下.条件变量(Condition Variable)是线程间的一种同步机制,提供给两个线程协同完成任务的一种方法,使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止.条件变量的测试一般是用互斥量来保护的,用来确保每

C++11 中的线程、锁和条件变量

转自:http://blog.jobbole.com/44409/ 线程 类std::thread代表一个可执行线程,使用时必须包含头文件<thread>.std::thread可以和普通函数,匿名函数和仿函数(一个实现了operator()函数的类)一同使用.另外,它允许向线程函数传递任意数量的参数. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <thread> void func() {    // do some work } int

【转】【C++】C++ 中的线程、锁和条件变量

线程 类std::thread代表一个可执行线程,使用时必须包含头文件<thread>.std::thread可以和普通函数,匿名函数和仿函数(一个实现了operator()函数的类)一同使用.另外,它允许向线程函数传递任意数量的参数. #include <thread> void func() { // do some work } int main() { std::thread t(func); t.join(); return 0; } 上例中,t 是一个线程对象,函数fu

线程三线程安全

线程三线程安全对于多线程的系统来说,如果不加以限制的话,会造成数据安全等问题,对于数据安全问题会有如下问题:1.读取脏数据 在读取数据的时候,数据被修改了,而程序读取的是修改过的或者没有修改过的,反正不是自己期望读取的数据的值.2.数据不一致 在使用多线程进行数据处理的时候,最终得出来的数据每一次执行都不一样.3.数据错乱 在数据的打印或者数组中,会造成数据的顺序错乱,对于关系可能会错乱.--对于数据的安全性,java提供了一些解决的方法:1.使用synchronized同步关键字2.使用vol

linux网络编程-----&gt;线程同步--&gt;条件变量

开发使用多线程过程中, 不可避免的会出现多个线程同时操作同一块共享资源, 当操作全部为读时, 不会出现未知结果, 一旦当某个线程操作中有写操作时, 就会出现数据不同步的事件. 而出现数据混乱的原因: 资源共享(独享资源则不会) 调试随机(对数据的访问会出现竞争) 线程间缺少必要的同步机制 以上三点, 前两点不能被改变. 欲提高效率, 传递数据, 资源必须共享. 只要资源共享, 就一定会出现线程间资源竞争, 只要存在竞争关系, 数据就会出现混乱. 所以只能从第三点着手, 使多个线程在访问共享资源的

线程同步-条件变量

条件变量的使用:将互斥量的忙等机制改为通知机制 涉及到的函数有以下几个: int pthread_cond_destroy(pthread_cond_t *cond); /********************** *功能:条件变量的初始化 *参数:cond:条件变量 * attr:条件变量的属性 * ********************/ int pthread_cond_init(pthread_cond_t *restrict cond , const pthread_condatt

使用线程间通信之条件变量

最近用C++写安卓下的一个通讯程序,作为jni库给java调用,采用多线程轮询遇到一个问题描述如下: A线程收到数据,放入队列,是生产者. B.C.D若干个线轮询训消息队列,如果队列有数据就取出进行处理,没数据就Sleep(T)休息,问题是这个T值取多大合适?取大了消息处理不及时,取小了手机cpu上升电池很快耗光. 这个问题最佳解法是采用条件变量,可以比较完美解决问题,以下代码使用C++封装,用win32 SDK的条件变量举例,Linux下有完全等价的概念: // 线程消息通知 class Th