POSIX线程库条件变量的使用——Pthreads线程库实例笔记4
2014-11-17 17:15
169 查看
转载:
http://www.cnblogs.com/blueclue/archive/2010/07/21/1780779.html
条件变量(Condition Variables)
条件变量是什么?
条件变量为我们提供了另一种线程间同步的方法,然而,互斥量是通过控制线程访问数据来实现同步,条件变量允许线程同步是基于实际数据的值。
如果没有条件变量,程序员需要让线程不断地轮询,以检查是否满足条件。由于线程处在一个不间断的忙碌状态,所以这是相当耗资源的。条件变量就是这么一个不需要轮询就可以解决这个问题的方法。
条件变量总是跟互斥锁(mutex lock)一起使用。
下面是使用条件变量的比较典型的过程:
创建和销毁条件变量
函数:
用法:
条件变量必须声明为pthread_cond_t类型,并且在使用前必须要初始化。初始化,有两种方法:
静态初始化,像这样声明:pthread_con_t myconvar = PTHREAD_CON_INITIALIZER;
动态初始化,使用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()阻塞调用线程,直到指定的条件变量收到信号。当互斥量被锁定时,应该调用这个函数,并且在等待时自动释放这个互斥量,在接收到信号后线程被唤醒,线程的互斥量会被自动锁定,程序员在线程中应当在此函数后解锁互斥量。
pthread_cond_signal()函数常用来发信号给(或唤醒)正在等待条件变量的另一个线程,在互斥量被锁定后应该调用这个函数,并且为了pthread_cond_wait()函数的完成必须要解锁互斥量。
如果多个线程处于阻塞等待状态,那么必须要使用pthreads_cond_broadcast()函数,而不是pthread_cond_signal()。
在调用pthread_cond_wait()函数之前调用pthread_cond_signal()函数是个逻辑上的错误,所以,在使用这些函数时,正确的锁定和解锁与条件变量相关的互斥量是非常必要的,例如:
在调用pthread_cond_wait()之前锁定互斥量失败,可致使其无法阻塞;
在调用pthread_cond_signal()之后解锁互斥量失败,则致使与之对应的pthread_cond_wait()函数无法完成,并仍保持阻塞状态。
实例分析
看到下面的一汪代码不要挠头,99行而已,之后会抽丝剥茧,目的是对条件变量的运行机制了解个大概:
实例代码
/******************************************************************************
* 描述:
* 应用Pthreads条件变量的实例代码,主线程创建三个线程,其中两个为“count”变量做
* 加法运算,第三个线程监视“count”的值。当“count”达到一个限定值,等待线程准备接收来
* 自于两个加法线程中一个的信号,等待 线程唤醒后更改“count”的值。程序继续运行直到加法
* 线程达到TCOUNT的值。最后,主程序打印出count的值。
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 3
#define TCOUNT 5 //单线程轮询次数
#define COUNT_LIMIT 7 //发送信号的次数
int count = 0; //全局的累加量
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
void *inc_count(void *t) {
int i;
long my_id = (long) t;
for (i = 0; i < TCOUNT; i++) {
pthread_mutex_lock(&count_mutex);
count++;
/*
* 检查count的值,如果条件满足就发信号给等待线程
* 注意,此处是用信号量锁定的。
* */
if (count < COUNT_LIMIT) {
printf("inc_count(): thread %ld, count = %d Threshold reached. ",
my_id, count);
pthread_cond_signal(&count_threshold_cv);
printf("Just sent signal.\n");
}
printf("inc_count(): thread %ld, count = %d, unlocking mutex\n", my_id,
count);
pthread_mutex_unlock(&count_mutex);
/*为线程轮询互斥锁增加延时*/
sleep(1);
}
pthread_exit(NULL);
}
void *watch_count(void *t) {
long my_id = (long) t;
printf("Starting watch_count(): thread %ld\n", my_id);
/*锁定互斥量并等待信号,注意,pthread_cond_wait函数在等待时将自动以自动原子方式
* 解锁互斥量。还有,请注意,如果等待线程运行到等待函数之前已经满足COUNT_LIMIT的
* 条件判断,轮询会忽略掉等待函数,
* */
while (count < COUNT_LIMIT) {
pthread_mutex_lock(&count_mutex);
printf("watch_count(): thread %ld going into wait...\n", my_id);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count(): thread %ld Condition signal received.\n", my_id);
printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
pthread_mutex_unlock(&count_mutex);
}
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
int i;
long t1 = 1, t2 = 2, t3 = 3;
pthread_t threads[3];
pthread_attr_t attr;
/*初始化互斥量和条件变量对象*/
pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init(&count_threshold_cv, NULL);
/*创建线程时设为可连接状态,便于移植*/
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void *) t1);
pthread_create(&threads[1], &attr, inc_count, (void *) t2);
pthread_create(&threads[2], &attr, inc_count, (void *) t3);
/* 等待所有线程完成*/
for (i = 1; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
/*发送信号给监听线程*/
pthread_cond_signal(&count_threshold_cv);
pthread_join(threads[0],NULL);
printf("Main(): Waited on %d threads. Final value of count = %d. Done.\n",
NUM_THREADS, count);
/*清除并退出 */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit(NULL);
}
主线程创建了3个子线程,一个线程用来监听信号(threads[0],调用watch_count()函数),两个线程用来发送信号(threads[1]、threads[2],调用inc_count()函数);
两个发送信号线程,主要负责两件事:
两个线程利用互斥量为count做加法运算,两个线程一起做了(2*TCOUNT=)10次运算;
count值小于COUNT_LIMIT时,发送信号给监听线程;
监听线程作用只有一个,就是如果count值小于COUNT_LIMIT则等待线程;
整体来讲,就是两个发送信号的线程让count做迭加运算,并在迭加到一定值之前给监听线程发送信号,监听线程收到打印信息
两个地方需要注意一下:
pthread_cond_wait()有解锁和锁定互斥量的操作,它所进行的操作大体有三步:解锁—阻塞监听—锁定,所以在监听线程的循环体里面有两次“锁定-解锁”的操作;
主函数main最后的pthread_cond_signal()这句必不可少,因为监听线程运转没有延时,在count的值达到COUNT_LIMIT-1时,已经处于waiting状态。
运行结果
http://www.cnblogs.com/blueclue/archive/2010/07/21/1780779.html
条件变量(Condition Variables)
条件变量是什么?
条件变量为我们提供了另一种线程间同步的方法,然而,互斥量是通过控制线程访问数据来实现同步,条件变量允许线程同步是基于实际数据的值。
如果没有条件变量,程序员需要让线程不断地轮询,以检查是否满足条件。由于线程处在一个不间断的忙碌状态,所以这是相当耗资源的。条件变量就是这么一个不需要轮询就可以解决这个问题的方法。
条件变量总是跟互斥锁(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类型,并且在使用前必须要初始化。初始化,有两种方法:
静态初始化,像这样声明:pthread_con_t myconvar = PTHREAD_CON_INITIALIZER;
动态初始化,使用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()函数是个逻辑上的错误,所以,在使用这些函数时,正确的锁定和解锁与条件变量相关的互斥量是非常必要的,例如:
在调用pthread_cond_wait()之前锁定互斥量失败,可致使其无法阻塞;
在调用pthread_cond_signal()之后解锁互斥量失败,则致使与之对应的pthread_cond_wait()函数无法完成,并仍保持阻塞状态。
实例分析
看到下面的一汪代码不要挠头,99行而已,之后会抽丝剥茧,目的是对条件变量的运行机制了解个大概:
实例代码
/******************************************************************************
* 描述:
* 应用Pthreads条件变量的实例代码,主线程创建三个线程,其中两个为“count”变量做
* 加法运算,第三个线程监视“count”的值。当“count”达到一个限定值,等待线程准备接收来
* 自于两个加法线程中一个的信号,等待 线程唤醒后更改“count”的值。程序继续运行直到加法
* 线程达到TCOUNT的值。最后,主程序打印出count的值。
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 3
#define TCOUNT 5 //单线程轮询次数
#define COUNT_LIMIT 7 //发送信号的次数
int count = 0; //全局的累加量
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
void *inc_count(void *t) {
int i;
long my_id = (long) t;
for (i = 0; i < TCOUNT; i++) {
pthread_mutex_lock(&count_mutex);
count++;
/*
* 检查count的值,如果条件满足就发信号给等待线程
* 注意,此处是用信号量锁定的。
* */
if (count < COUNT_LIMIT) {
printf("inc_count(): thread %ld, count = %d Threshold reached. ",
my_id, count);
pthread_cond_signal(&count_threshold_cv);
printf("Just sent signal.\n");
}
printf("inc_count(): thread %ld, count = %d, unlocking mutex\n", my_id,
count);
pthread_mutex_unlock(&count_mutex);
/*为线程轮询互斥锁增加延时*/
sleep(1);
}
pthread_exit(NULL);
}
void *watch_count(void *t) {
long my_id = (long) t;
printf("Starting watch_count(): thread %ld\n", my_id);
/*锁定互斥量并等待信号,注意,pthread_cond_wait函数在等待时将自动以自动原子方式
* 解锁互斥量。还有,请注意,如果等待线程运行到等待函数之前已经满足COUNT_LIMIT的
* 条件判断,轮询会忽略掉等待函数,
* */
while (count < COUNT_LIMIT) {
pthread_mutex_lock(&count_mutex);
printf("watch_count(): thread %ld going into wait...\n", my_id);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count(): thread %ld Condition signal received.\n", my_id);
printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
pthread_mutex_unlock(&count_mutex);
}
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
int i;
long t1 = 1, t2 = 2, t3 = 3;
pthread_t threads[3];
pthread_attr_t attr;
/*初始化互斥量和条件变量对象*/
pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init(&count_threshold_cv, NULL);
/*创建线程时设为可连接状态,便于移植*/
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void *) t1);
pthread_create(&threads[1], &attr, inc_count, (void *) t2);
pthread_create(&threads[2], &attr, inc_count, (void *) t3);
/* 等待所有线程完成*/
for (i = 1; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
/*发送信号给监听线程*/
pthread_cond_signal(&count_threshold_cv);
pthread_join(threads[0],NULL);
printf("Main(): Waited on %d threads. Final value of count = %d. Done.\n",
NUM_THREADS, count);
/*清除并退出 */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit(NULL);
}
主线程创建了3个子线程,一个线程用来监听信号(threads[0],调用watch_count()函数),两个线程用来发送信号(threads[1]、threads[2],调用inc_count()函数);
两个发送信号线程,主要负责两件事:
两个线程利用互斥量为count做加法运算,两个线程一起做了(2*TCOUNT=)10次运算;
count值小于COUNT_LIMIT时,发送信号给监听线程;
监听线程作用只有一个,就是如果count值小于COUNT_LIMIT则等待线程;
整体来讲,就是两个发送信号的线程让count做迭加运算,并在迭加到一定值之前给监听线程发送信号,监听线程收到打印信息
两个地方需要注意一下:
pthread_cond_wait()有解锁和锁定互斥量的操作,它所进行的操作大体有三步:解锁—阻塞监听—锁定,所以在监听线程的循环体里面有两次“锁定-解锁”的操作;
主函数main最后的pthread_cond_signal()这句必不可少,因为监听线程运转没有延时,在count的值达到COUNT_LIMIT-1时,已经处于waiting状态。
运行结果
相关文章推荐
- POSIX线程库条件变量的使用——Pthreads线程库实例笔记4
- POSIX线程互斥量的使用——pthreads线程库实例笔记3
- c++学习笔记-map的使用实例(单词转换)
- 【Android】学习笔记(13)——AsyncTask使用实例
- Qt4精彩实例分析笔记--标准对话框的使用
- j2me学习笔记【8】——TextField的使用实例
- j2me学习笔记【9】——List中复选框的使用实例
- POSIX线程的创建与取消—pthreads线程库实例笔记1
- DB2新手使用的一些小笔记:新建实例、数据库路径不存在、客户端连接 .
- (学习笔记)简单正则表达式的使用实例[javascript]
- 基础学习笔记之opencv(18):kmeans函数使用实例
- oracle cursor使用的几个实例(笔记)
- Jpcap包的学习笔记(五)如何使用JpcapCaptor实例来捕捉网络数据包(上部)
- ibatis学习笔记(四)>>>>>>>ibatis使用实例
- 基础学习笔记之opencv(18):kmeans函数使用实例
- 面向对象的程序设计-学习笔记-28-虚函数的使用(实例补充)
- DB2新手使用的一些小笔记:新建实例、数据库路径不存在、客户端连接
- c#的ThreadPool使用笔记(四)--实例:端口扫描程序
- j2me学习笔记【15】——使用低层用户界面显示文本小实例
- POSIX线程的创建与取消—pthreads线程库实例笔记1