条件变量 |
概述
- 条件变量提供 了另一个线程同步的方法,当mutex通过控制存取数据来实现线程同步时,条件变量允许线程基于实际的变量值来实现线程同步。
- 没有条件变量,开发者需要线程不停的轮询以查询条件是否满足,因为线程要不停的忙等,会消耗很多资源。条件变量就是不用轮询而能达到同样目的方法。
- 条件变量总是与mutex锁联系在一起。
- 下面是一个使用条件变量的代表性序列
Main Thread o 申明和初始化需要同步的全局数据/变量(比如:count) o 申明和初始化一个条件变量对象 o 申明和初始化相关联的mutex o 创建threads A 和B并运行它们 | |
Thread A o Do work up to the point where a certain condition must occur (such as "count" must reach a specified value) o Lock associated mutex and check value of a global variable o Call pthread_cond_wait() to perform a blocking wait for signal from Thread-B. Note that a call to pthread_cond_wait() automatically and atomically unlocks the associated mutex variable so that it can be used by Thread-B. o When signalled, wake up. Mutex is automatically and atomically locked. o Explicitly unlock mutex o Continue | Thread B o Do work o Lock associated mutex o Change the value of the global variable that Thread-A is waiting upon. o Check value of the global Thread-A wait variable. If it fulfills the desired condition, signal Thread-A. o Unlock mutex. o Continue |
Main Thread Join / Continue |
条件变量 |
创建和销毁条件变量
函数:
pthread_cond_init (condition,attr) pthread_cond_destroy (condition) pthread_condattr_init (attr) pthread_condattr_destroy (attr) |
用法:
- 条件变量必须用pthread_cond_t 类型来声明,而且在使用之前必须初始化。在这里有两种方法初始化条件变量:
- 静态的方式初始化,例如:
pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER; - 动态的方式,利用pthread_cond_init()进行初始化,被创建的条件变量ID通过condition 参数返回给调用线程。这种方法允许条件变量设置属性attr 。
- 静态的方式初始化,例如:
- 可选的attr 用来设置条件变量属性,对于条件变量,这里仅仅只定义了一个属性:process-shared ,它允条件变量可以被其他进程中的线程访问。如果使用此条件变量属性,必须把它定义为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()会阻塞调用的线程,直到特定的条件(condition)满足,当这个线程运行时,mutex会被 加锁,当它阻塞时mutex会自动解锁。当收到信号唤醒线程时,mutex会被线程自动上锁, 当线程完成更新共享数据后,开发者有责任解锁mutex。
- pthread_cond_signal()用来通知(唤醒)等待在条件变量上的另一线程,在mutex 被加锁后被调用,在完成pthread_cond_wait()运行后必须解锁mutex。
- 如果多于一个线程处于阻塞状态,应该用pthread_cond_broadcast()代替pthread_cond_signal()。
- 如果在调用pthread_cond_wait()前先调用pthread_cond_signal()就是逻辑错误。
| 当使用这些例程时,正确的加锁和解锁一个相关联的mutex是必须的,例如:
|
Example: 使用条件变量
Example Code - Using Condition Variables This simple example code demonstrates the use of several Pthread condition variable routines. The main routine creates three threads. Two of the threads perform work and update a "count" variable. The third thread waits until the count variable reaches a specified value. #include <pthread.h> #include <stdio.h>
#define NUM_THREADS 3 #define TCOUNT 10 #define COUNT_LIMIT 12
int count = 0; int thread_ids[3] = {0,1,2}; pthread_mutex_t count_mutex; pthread_cond_t count_threshold_cv;
void *inc_count(void *idp) { int j,i; double result=0.0; int *my_id = idp;
for (i=0; i<TCOUNT; i++) { pthread_mutex_lock(&count_mutex); count++;
/* Check the value of count and signal waiting thread when condition is reached. Note that this occurs while mutex is locked. */ if (count == COUNT_LIMIT) { pthread_cond_signal(&count_threshold_cv); printf("inc_count(): thread %d, count = %d Threshold reached./n", *my_id, count); } printf("inc_count(): thread %d, count = %d, unlocking mutex/n", *my_id, count); pthread_mutex_unlock(&count_mutex);
/* Do some work so threads can alternate on mutex lock */ for (j=0; j<1000; j++) result = result + (double)random(); } pthread_exit(NULL); }
void *watch_count(void *idp) { int *my_id = idp;
printf("Starting watch_count(): thread %d/n", *my_id);
/* Lock mutex and wait for signal. Note that the pthread_cond_wait routine will automatically and atomically unlock mutex while it waits. Also, note that if COUNT_LIMIT is reached before this routine is run by the waiting thread, the loop will be skipped to prevent pthread_cond_wait from never returning. */ pthread_mutex_lock(&count_mutex); if (count<COUNT_LIMIT) { pthread_cond_wait(&count_threshold_cv, &count_mutex); printf("watch_count(): thread %d Condition signal received./n", *my_id); } pthread_mutex_unlock(&count_mutex); pthread_exit(NULL); }
int main (int argc, char *argv[]) { int i, rc; pthread_t threads[3]; pthread_attr_t attr;
/* Initialize mutex and condition variable objects */ pthread_mutex_init(&count_mutex, NULL); pthread_cond_init (&count_threshold_cv, NULL);
/* For portability, explicitly create threads in a joinable state */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&threads[0], &attr, inc_count, (void *)&thread_ids[0]); pthread_create(&threads[1], &attr, inc_count, (void *)&thread_ids[1]); pthread_create(&threads[2], &attr, watch_count, (void *)&thread_ids[2]);
/* Wait for all threads to complete */ for (i=0; i<NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf ("Main(): Waited on %d threads. Done./n", NUM_THREADS);
/* Clean up and exit */ pthread_attr_destroy(&attr); pthread_mutex_destroy(&count_mutex); pthread_cond_destroy(&count_threshold_cv); pthread_exit(NULL);
} ――――――――――――――――――――――――――――――――― 输出: inc_count(): thread 0, count = 1, unlocking mutex
Starting watch_count(): thread 2
inc_count(): thread 1, count = 2, unlocking mutex
inc_count(): thread 0, count = 3, unlocking mutex
inc_count(): thread 1, count = 4, unlocking mutex
inc_count(): thread 0, count = 5, unlocking mutex
inc_count(): thread 0, count = 6, unlocking mutex
inc_count(): thread 1, count = 7, unlocking mutex
inc_count(): thread 0, count = 8, unlocking mutex
inc_count(): thread 1, count = 9, unlocking mutex
inc_count(): thread 0, count = 10, unlocking mutex
inc_count(): thread 1, count = 11, unlocking mutex
inc_count(): thread 0, count = 12 Threshold reached.
inc_count(): thread 0, count = 12, unlocking mutex
watch_count(): thread 2 Condition signal received.
inc_count(): thread 1, count = 13, unlocking mutex
inc_count(): thread 0, count = 14, unlocking mutex
inc_count(): thread 1, count = 15, unlocking mutex
inc_count(): thread 0, count = 16, unlocking mutex
inc_count(): thread 1, count = 17, unlocking mutex
inc_count(): thread 0, count = 18, unlocking mutex
inc_count(): thread 1, count = 19, unlocking mutex
inc_count(): thread 1, count = 20, unlocking mutex
Main(): Waited on 3 threads. Done. |
LLNL 特定信息和建议 |
本节介绍了Livermore Computing's systems的特有细节。
应用:
- 所有的LC production systems 包含了POSIX版本10(最后版本)的Pthreads实现,它是首选的实现。
- 实现的区别在于能够创建的最大线程数目和它们的缺省栈大小。
编译:
- LC 维护一些编译器,不同的版本信息可以查看 LC's Supported Compilers。
- 应用于LC编译器的命令描述在 Compiling Threaded Programs。
Mixing MPI with Pthreads:
- 在MPI和Pthreads都存在的环境中编程是一样的,在所有LC系统中很容易实现。
- 设计:
- 每个MPI进程典型的创建和管理N个线程,这里的N是能够最大限度发挥CPU/node 功能的线程数。
- 最佳的N会随着你的平台和你的应用特征而变化。
- 对于在节点间有通信适配层的IBM SP系统,在每个节点能够提供更加有效的2个(或者多个)MPI任务.
- 一般来说,如果多线程调用MPI可能会有问题,如果MPI调用需要在线程中调用,应该保证只在一个线程中。
- 编译:
- 针对平台和语言选择适当的MPI编译命令。
- 注意要包含必须的标志 (如:-pthread or -qnosave) 。
- MPICH不是线程安全的。
- 下面是应用和Pthreads的示例:
- Serial
- Pthreads only
- MPI only
- MPI with pthreads
- makefile (for IBM SP)
没有提及的方面 |
有几个Pthreads API 的特征在本指南中没有提及,把它们列在下面:你可以从Pthread Library Routines Reference部分找到相应的信息。
- Thread 调度
- 调度会随着具体的实现而不同,在大多数时候,缺省的调度机制已经足够了。
- Pthreads API提供的例程可以显式的设置调度策略和优先级,这些设置会覆盖缺省的设置。
- API并不要求一定实现这些功能。
- Keys: 线程特有数据
- 随着线程从不同的例程调用和返回,本地数据会随之申请和销毁。
- 为了保存栈数据,通常可以把它作为参数从一个例程传递到下一例程,或者把它保存到一个线程的全局变量中。
- Pthreads 可能提供另外的方便且通用的方法去获取这些keys.
- 为了处理优先级倒置("priority inversion")问题而需要的Mutex Protocol属性和Mutex 优先级管理
- 条件变量的进程间共享
- 注销线程
- 线程和信号
Pthread 库例程参考 |
Pthread Functions | |
Thread Management | |
Thread-Specific Data | |
Thread Cancellation | |
pthread_getcancelstate | |
Thread Scheduling | |
Signals | |
Pthread Attribute Functions | |
Basic Management | |
Detachable or Joinable | |
Specifying Stack Information | |
Thread Scheduling Attributes | |
Mutex Functions | |
Mutex Management | |
Priority Management | |
Mutex Attribute Functions | |
Basic Management | |
Sharing | |
Protocol Attributes | |
Priority Management | |
Condition Variable Functions | |
Basic Management | |
Condition Variable Attribute Functions | |
Basic Management | |
Sharing | |
下面是一些练习与其他信息
参考和更多的信息 |
- Author: Blaise Barney, Livermore Computing.
- "Pthreads Programming". B. Nichols et al. O'Reilly and Associates.
- "Threads Primer". B. Lewis and D. Berg. Prentice Hall
- "Programming With POSIX Threads". D. Butenhof. Addison Wesley
www.awl.com/cseng/titles/0-201-63392-2 - "Programming With Threads". S. Kleiman et al. Prentice Hall
(完)
-------------------------------------