前提蜕变变量也是出自POSIX线程尺度,另外一种线程同步机制,。首要用来等待某个前提的产生。能够用来同步统一进程中的各个线程。固然若是一个前提变量存放在多个进程同享的某个内存区中,那末还能够经由过程前提变量来中止进程间的同步。
每一个前提变量老是和一个互斥量相干联,前提本人是由互斥量庇护的,线程在改动前提情况之间必需求锁住互斥量。前提变量相对互斥量最年夜的优点在于答应线程以无竞争的格式等待前提的产生。当一个线程获得互斥锁后,发现自身需求等待某个前提变成真,若是是多么,该线程便能够等待在某个前提上,多么就不需求经由过程轮询的格式来断定添加,年夜年夜节流了CPU时分。
在互斥量一文中说过:互斥量是用于上锁,而不是用于等待;此刻这句话能够加强为:互斥量是用于上锁,前提变量用于等待;
前提变量声明为pthread_cond_t数据类型,在 bits/pthreadtypes.h 中有细致的界说。
1、前提变量初始化和烧毁
/* Initialize condition variable */
int pthread_cond_init (pthread_cond_t *__restrict __cond,
__const pthread_condattr_t *__restrict __cond_attr) ;
/* Destroy condition variable */
int pthread_cond_destroy (pthread_cond_t *__cond);
下面两个函数分别由于前提变量的初始化和烧毁。
和互斥量的初始化一样,若是前提变量是静态分派的,能够经由过程常量中止初始化,以下:
pthread_cond_t mlock = PTHREAD_COND_INITIALIZER;
也可以经由过程pthread_cond_init()中止初始化,对静态分派的前提变量由于不克不及直接赋值中止初始化,就只能采取这类格式中止初始化。那末当不在需求应用前提变量时,需求挪用pthread_cond_destroy()烧毁该前提所占用的资本。
2、前提变量的属性设置
/* 初始化前提变量属性对象 */
int pthread_condattr_init (pthread_condattr_t *__attr);
/* 烧毁前提变量属性对象 */
int pthread_condattr_destroy (pthread_condattr_t *__attr);
/* 取得前提变量属性对象在进程间同享与否的标识 */
int pthread_condattr_getpshared (__const pthread_condattr_t * __restrict __attr,
int *__restrict __pshared);
/* 设置前提变量属性对象,标识在进程间同享与否 */
int pthread_condattr_setpshared (pthread_condattr_t *__attr, int __pshared) ;
这个属性的设置和互斥量属性设置是一样的,细致应用能够参考互斥量的用法:互斥量的属性设置。
3、前提变量的应用
/* 等待前提变成真 */
int pthread_cond_wait (pthread_cond_t *__restrict __cond,
pthread_mutex_t *__restrict __mutex);
/* 限时等待前提为真 */
int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
pthread_mutex_t *__restrict __mutex,
__const struct timespec *__restrict __abstime);
/* 叫醒一个等待前提的线程. */
int pthread_cond_signal (pthread_cond_t *__cond);
nbsp;/* 叫醒等待该前提的一切线程 */
int pthread_cond_broadcast (pthread_cond_t *__cond);
(1)pthread_cond_wait()函数用于等待前提被触发。该函数传进两个参数,一个前提变量一个互斥量,函数将前提变量和互斥量中止联络关系,互斥量对该前提中止庇护,传进的互斥量必需是已锁住的。挪用pthread_cond_wait()函数后,会原子的实行以下两个举措:
● 将挪用线程放到等待前提的线程列表上,即进入眠眠;
● 对互斥量中止解锁;
由于这两个支配时原子支配,多么就封锁了前提查抄和线程进入眠眠等待前提改动这两个支配之间的时分通道,多么就不会错过任何前提的转变。
当pthread_cond_wait()前往后,互斥量会再次被锁住。
(2)pthread_cond_timedwait()函数和pthread_cond_wait()的任务格式相似,只是多了一个等待时分。等待时分的规划为struct timespec,
struct timespec{
time_t tv_sec //Seconds.
long tv_nsec //Nanoseconds.
};
函数请求传进的时分值是一个尽对值,不是绝对值,例如,想要等待3分钟,必需先获得以后时分,然后加上3分钟。
要想获得以后系统时分的timespec值,没有直接可挪用的函数,需求经由过程挪用gettimeofday函数取得timeval规划,然后转换成timespec规划,转换公式就是:
timeSpec.tv_sec = timeVal.tv_sec;
timeSpec.tv_nsec = timeVal.tv_usec * 1000;
所以要等待3分钟,timespec时分规划的获得应当以下所示:
struct timeval now;
struct timespec until;
gettimeofday( now);//获得系统以后时分
//把时分从timeval规划转换成timespec规划
until.tv_sec = now.tv_sec;
until.tv_nsec = now.tv_usec * 1000;
//添加min
until.tv_sec += 3 * 60;
若是时分到后,前提还没有产生,那末会前往ETIMEDOUT缺点。
从pthread_cond_wait()和pthread_cond_timewait()胜利前往时,线程需求从头计较前提,由于其他线程可以在运转进程中已改动前提。
(3)pthread_cond_signal() pthread_cond_broadcast()
这两个函数都是用于向等待前提的线程发送叫醒旌旗灯号,pthread_cond_signal()函数只会叫醒等待该前提的某个线程,pthread_cond_broadcast()会广播前提情况的改动,以叫醒等待该前提的一切线程。例如多个线程只读同享资本,这是能够将它们都叫醒。
这里要注重的是:必然要在改动前提情况后,再给线程发送旌旗灯号。
推敲前提变量旌旗灯号单播发送和广播发送的一种候选格式是对峙应用广播发送。只需在等待者代码编写得当,只需一个等待者需求叫醒,且叫醒哪一个线程无所谓,那末此时为这类环境应用单播,所以其他环境下都必需应用广播发送。
上面是一个测试代码,摹拟同步标题中经典的出产者消费者标题。
#include iostream
#include queue
#include cstdlib
#include unistd.h
#include pthread.h
using namespace std;
//把同享数据和它们的同步变量调集到一个规划中,这常常是一个较好的编程技艺。
struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
queue int product;
}sharedData = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
void * produce(void *ptr)
{
for (int i = 0; i ++i)
{
pthread_mutex_lock( sharedData.mutex);
sharedData.product.push(i);
pthread_mutex_unlock( sharedData.mutex);
if (sharedData.product.size() == 1)
pthread_cond_signal( sharedData.cond);
//sleep(1);
}
}
void * consume(void *ptr)
{
for (int i = 0; i )
{
pthread_mutex_lock( sharedData.mutex);
while(sharedData.product.empty())
pthread_cond_wait( sharedData.cond, sharedData.mutex);
++i;
cout consume: sharedData.product.front() endl;
sharedData.product.pop();
pthread_mutex_unlock( sharedData.mutex);
//sleep(1);
}
}
int main()
{
pthread_t tid1, tid2;
pthread_create( tid1, NULL, consume, NULL);
pthread_create( tid2, NULL, produce, NULL);
void *retVal;
pthread_join(tid1, retVal);
pthread_join(tid2, retVal);
return 0;
}
法式的运转成果以下所示:
consume:0
consume:1
consume:2
consume:3
consume:4
consume:5
consume:6
consume:7
consume:8
consume:9