深入Phtread(三):线程的同步-Condition Variables

继续昨天的线程同步,条件变量(Condition Variables)是用于线程间,通信共享数据状态改变的机制。
简介
条件变量的创建和销毁
等待条件变量
唤醒等待条件变量的线程
简介
    当线程互斥地访问一些共享的状态时,往往会有些线程需要等到这些状态改变后才应该继续执行。如:有一个共享的队列,一个线程往队列里面插入数据,另一个线程从队列中取数据,当队列为空的时候,后者应该等待队列里面有值才能取数据。而共享数据(队列)应该用mutex来保护,为了检查共享数据的状态(队列是否为空),线程必须先锁定mutex,然后检查,最后解锁mutex。
 
    问题出来了:当另外一个线程B锁定mutex后,往队列里面插入了一个值,B并不知道A在等着它往队列里面放入一个值。,线程A(等待状态改变)一直在运行,线程B可能已经检查过队列是空的,并不知道队列里已经有值了,所以一直阻塞着自己。为了解决这样的问题引入了条件变量机制。线程B等待于一个条件变量,当线程A插入了一个值后,signal或broadcast这个条件变量,通知线程B状态已改变,A发现条件变量被signaled了,就继续执行。就这样,当一个线程改变共享数据状态后,可以及时通知那些等待于该状态的线程。图示下:
 


    中间的矩形代表条件变量,当线程线位于矩形内,表示线程等待该条件变量。位于中心线下下方,则表示signal了该条件变量。
    开始线程1 signal 了条件变量,由于没有其他线程等待于该条件变量,所以没什么效果。然后,线程1和线程2先后等待该条件变量,过了一会,线程3 signal了条件变量,线程3的信号解除了线程1的阻塞。然后,线程3等待该条件变量。最后线程1 broadcast了该条件变量,同时解除了等待于条件变量的线程1和线程2。
 

条件变量的创建和销毁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* condattr);
int pthread_cond_destroy(pthread_cond_t* cond);
 
和互斥量一样,可以动态创建和静态创建。
静态创建:条件变量声明为extern或static变量时。
例程:
+ expand sourceview plaincopy to clipboardprint?
#include <pthread.h>  
#include "error.h"  
 
typedef struct my_struct_tag  
{  
    pthread_mutex_t mutex;  
    pthread_cond_t cond;  
    int value;  
} my_struct_t;  
 
my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};  
 
int main()  
{  
    return 0;  

#include <pthread.h>
#include "error.h"

typedef struct my_struct_tag
{
 pthread_mutex_t mutex;
 pthread_cond_t cond;
 int value;
} my_struct_t;

my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};

int main()
{
 return 0;
}
 
 
动态创建:一般情况下,条件变量要和它的判定条件定义在一起,此时若包含该条件变量的数据动态创建了,则条件变量也需要动态创建,不过记得不用时用pthread_cond_destroy销毁。
 
例程:
+ expand sourceview plaincopy to clipboardprint?
#include <pthread.h>  
#include "error.h"  
 
typedef struct my_struct_tag  
{  
    pthread_mutex_t mutex;  
    pthread_cond_t cond;  
    int value;  
} my_struct_t;  
 
int main()  
{  
    my_struct_t* data;  
    data = (my_struct_t*)malloc(sizeof(my_struct_t));  
    if(data == NULL)  
        ERROR_ABORT(errno,"Allocate structure");  
 
    int status;  
    status = pthread_mutex_init(&data->mutex, NULL);  
    if(status != 0)  
        ERROR_ABORT(status, "Initial mutex");  
    status = pthread_cond_init(&data->cond, NULL);  
    if(status != 0)  
        ERROR_ABORT(status, "Initial condition");  
 
    /* .... */ 
      
    status = pthread_cond_destroy(&data->cond);  
    if(status != 0)  
        ERROR_ABORT(status, "Destroy cond");  
    status = pthread_mutex_destroy(&data->mutex);  
    if(status != 0)  
        ERROR_ABORT(status, "Destroy mutex");  
 
    free(data);  
 
    return 0;  

#include <pthread.h>
#include "error.h"

typedef struct my_struct_tag
{
 pthread_mutex_t mutex;
 pthread_cond_t cond;
 int value;
} my_struct_t;

int main()
{
 my_struct_t* data;
 data = (my_struct_t*)malloc(sizeof(my_struct_t));
 if(data == NULL)
  ERROR_ABORT(errno,"Allocate structure");

 int status;
 status = pthread_mutex_init(&data->mutex, NULL);
 if(status != 0)
  ERROR_ABORT(status, "Initial mutex");
 status = pthread_cond_init(&data->cond, NULL);
 if(status != 0)
  ERROR_ABORT(status, "Initial condition");

 /* .... */
 
 status = pthread_cond_destroy(&data->cond);
 if(status != 0)
  ERROR_ABORT(status, "Destroy cond");
 status = pthread_mutex_destroy(&data->mutex);
 if(status != 0)
  ERROR_ABORT(status, "Destroy mutex");

 free(data);

 return 0;
}
 
等待条件变量
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, struct timespec* expiration);
 
    条件变量与互斥量一起使用,调用pthread_cond_wait或pthread_cond_timedwait时,记得在前面锁定mutex,尽可能多的判断判定条件。上面提到的两个等待条件变量的函数,显示解锁mutex,然后阻塞线程等待状态改变,等待的条件变量signaled后,锁定mutex,返回。记着,这两个函数返回时,mutex一定是锁定的。
 
多个条件变量可以共享一个互斥变量,相反则不成立。
 
例程:
+ expand sourceview plaincopy to clipboardprint?
#include <pthread.h>  
#include <time.h>  
#include "error.h"  
#include <errno.h>  
 
typedef struct my_struct_tag  
{  
    pthread_mutex_t mutex;  
    pthread_cond_t cond;  
    int value;  
} my_struct_t;  
 
my_struct_t data = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};  
 
int hibernation = 1;  
 
void* wait_thread(void* arg)  
{  
    int  status;  
    sleep(hibernation);  
 
    status = pthread_mutex_lock(&data.mutex);  
    if(status != 0)  
        ERROR_ABORT(status, "Lock mutex");  
 
    data.value = 1;  
    status = pthread_cond_signal(&data.cond);  
    if(status != 0)  
        ERROR_ABORT(status, "Singal cond");  
 
    status = pthread_mutex_unlock(&data.mutex);  
    if(status != 0)  
        ERROR_ABORT(status, "Unlock mutex");  
 
    return NULL;  
}  
 
int main(int argc, char* argv[])  
{  
    pthread_t tid;  
    int status;  
    struct timespec timeout;  
 
    if(argc > 1)  
        hibernation = atoi(argv[1]);  
 
    status = pthread_create(&tid, NULL, wait_thread, NULL);  
    if(status != 0)  
        ERROR_ABORT(status, "Create wait thread");  
 
    timeout.tv_sec = time(NULL) + 2;  
    timeout.tv_nsec = 0;  
 
    status = pthread_mutex_lock(&data.mutex);  
    if(status != 0)  
        ERROR_ABORT(status, "Lock mutex");  
 
    while(data.value == 0)  
    {  
        status = pthread_cond_timedwait(&data.cond, &data.mutex, &timeout);  
        if(status == ETIMEDOUT)  
        {  
            printf("Condition wait timed out./n");  
            break;  
        }else 
        if(status != 0)  
            ERROR_ABORT(status, "timewait");  
    }  
 
    if(data.value != 0)  
        printf("Condition wa signaled!/n");  
 
    status = pthread_mutex_unlock(&data.mutex);  
    if(status != 0)  
        ERROR_ABORT(status, "Unlock mutex");  

#include <pthread.h>
#include <time.h>
#include "error.h"
#include <errno.h>

typedef struct my_struct_tag
{
 pthread_mutex_t mutex;
 pthread_cond_t cond;
 int value;
} my_struct_t;

my_struct_t data = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};

int hibernation = 1;

void* wait_thread(void* arg)
{
 int  status;
 sleep(hibernation);

 status = pthread_mutex_lock(&data.mutex);
 if(status != 0)
  ERROR_ABORT(status, "Lock mutex");

 data.value = 1;
 status = pthread_cond_signal(&data.cond);
 if(status != 0)
  ERROR_ABORT(status, "Singal cond");

 status = pthread_mutex_unlock(&data.mutex);
 if(status != 0)
  ERROR_ABORT(status, "Unlock mutex");

 return NULL;
}

int main(int argc, char* argv[])
{
 pthread_t tid;
 int status;
 struct timespec timeout;

 if(argc > 1)
  hibernation = atoi(argv[1]);

 status = pthread_create(&tid, NULL, wait_thread, NULL);
 if(status != 0)
  ERROR_ABORT(status, "Create wait thread");

 timeout.tv_sec = time(NULL) + 2;
 timeout.tv_nsec = 0;

 status = pthread_mutex_lock(&data.mutex);
 if(status != 0)
  ERROR_ABORT(status, "Lock mutex");

 while(data.value == 0)
 {
  status = pthread_cond_timedwait(&data.cond, &data.mutex, &timeout);
  if(status == ETIMEDOUT)
  {
   printf("Condition wait timed out./n");
   break;
  }else
  if(status != 0)
   ERROR_ABORT(status, "timewait");
 }

 if(data.value != 0)
  printf("Condition wa signaled!/n");

 status = pthread_mutex_unlock(&data.mutex);
 if(status != 0)
  ERROR_ABORT(status, "Unlock mutex");
}
 
 
唤醒等待条件变量的线程
 
int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);
 
    一但有线程由于某些判定条件(predicate)没满足,等待条件变量。我们就有必要当条件满足时,发送信号去唤醒这些线程。
    注意:broadcast通常很容易被认为是signal的通用版,其实不能这样理解,准确一点应该说,signal是broadcast的优化版。具体区别不大,但signal效率较broadcast高些。但你不确信有几个线程等待条件变量时用broadcast(When in doubt, broadcast!)。
 
例程:

 + expand sourceview plaincopy to clipboardprint?
#include "error.h"  
#include <pthread.h>  
#include <time.h>  
#include <string.h>  
#include <errno.h>  
 
typedef struct alarm_tag  
{  
    struct alarm_tag* link;  
    int seconds;  
    time_t time;  
    char message[64];  
} alarm_t;  
 
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;  
pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER;  
alarm_t* alarm_list = NULL;  
time_t current_alarm = 0;  
 
/** 
 * alarm_mutex need to be locked   
 */ 
void alarm_insert(alarm_t* alarm)  
{  
    int status;  
 
    alarm_t* next;  
    alarm_t** last;  
    last = &alarm_list;  
    next = *last;  
 
    while(next != NULL)  
    {  
        if(next->time >= alarm->time)  
        {  
            alarm->link = next;  
            *last = alarm;  
            break;  
        }  
 
        last = &next->link;  
        next = next->link;  
    }  
 
    if(next == NULL){  
        *last = alarm;  
        alarm->link = NULL;  
    }  
 
    /*for test: output the list*/ 
    printf("[list: ");  
    for(next = alarm_list; next != NULL; next = next->link)  
    {  
        printf("%d(%d)[/"%s/"]  ", next->time, next->time-time(NULL), next->message);  
    }  
    printf("]/n");  
 
    if(current_alarm ==0  || alarm->time < current_alarm)  
    {  
        current_alarm = alarm->time;  
        status = pthread_cond_signal(&alarm_cond);  
        if(status != 0)  
            ERROR_ABORT(status,"Signal cond");  
    }  
 
}  
 
void* alarm_thread(void* arg)  
{  
    alarm_t* alarm;  
    int sleep_time;  
    time_t now;  
    int status, expired;  
    struct timespec cond_time;  
 
    while(1)  
    {  
        status = pthread_mutex_lock(&alarm_mutex);  
        if(status != 0)  
            ERROR_ABORT(status, "lock");  
 
        current_alarm = 0;  
 
        while(alarm_list == NULL)  
        {  
            status = pthread_cond_wait(&alarm_cond, &alarm_mutex);  
            if(status != 0 )  
                ERROR_ABORT(status, "Wait cond");  
        }  
 
        alarm = alarm_list;  
        alarm_list = alarm->link;  
        now = time(NULL);  
        expired = 0;  
 
        if(alarm->time > now)  
        {  
            printf("[wating: %d(%d)/"%s/"]/n", alarm->time, alarm->time - time(NULL), alarm->message);  
 
            cond_time.tv_sec = alarm->time;  
            cond_time.tv_nsec = 0;  
            current_alarm = alarm->time;  
            while(current_alarm == alarm->time)  
            {  
                status = pthread_cond_timedwait(&alarm_cond, &alarm_mutex,&cond_time);  
                if(status == ETIMEDOUT)  
                {  
                    expired = 1;  
                    break;  
                }  
            }  
 
            if(!expired)  
                alarm_insert(alarm);  
        }else 
            expired = 1;  
 
        if(expired)  
        {  
            printf("(%d) %s/n", alarm->seconds, alarm->message);  
            free(alarm);  
        }  
 
        status = pthread_mutex_unlock(&alarm_mutex);  
        if(status != 0)  
            ERROR_ABORT(status, "Unlock mutex");  
    }  
 
    return 0;  
}  
 
int main()  
{  
    pthread_t pid;  
    int status;  
    char line[128];  
 
    status = pthread_create(&pid, NULL, alarm_thread, NULL);  
    if(status != 0)  
        ERROR_ABORT(status, "pthread_create");  
 
    while(1)  
    {  
        fprintf(stdout, "Alarm>");  
        fgets(line, sizeof(line), stdin);  
        if(strlen(line) <= 0)  
            continue;  
 
        alarm_t* alarm = (alarm_t*)malloc(sizeof(alarm_t));  
        if(alarm == NULL)  
            ERROR_ABORT(errno,"memory can't allocated!");  
 
        if(sscanf(line, "%d %s", &alarm->seconds, alarm->message) != 2)  
        {  
            printf("Bad Command/n");  
            free(alarm);  
            continue;  
        }  
 
        status = pthread_mutex_lock(&alarm_mutex);  
        if(status != 0)  
            ERROR_ABORT(status, "pthread mutex locking..");  
 
        alarm->time = time(NULL) + alarm->seconds;  
 
        /* insert into list*/ 
 
        alarm_insert(alarm);  
 
        status = pthread_mutex_unlock(&alarm_mutex);  
        if(status != 0)  
            ERROR_ABORT(status, "pthread mutex unlocking...");  
    }  
 
    return 0;  

#include "error.h"
#include <pthread.h>
#include <time.h>
#include <string.h>
#include <errno.h>

typedef struct alarm_tag
{
 struct alarm_tag* link;
 int seconds;
 time_t time;
 char message[64];
} alarm_t;

pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER;
alarm_t* alarm_list = NULL;
time_t current_alarm = 0;

/**
 * alarm_mutex need to be locked 
 */
void alarm_insert(alarm_t* alarm)
{
 int status;

 alarm_t* next;
 alarm_t** last;
 last = &alarm_list;
 next = *last;

 while(next != NULL)
 {
  if(next->time >= alarm->time)
  {
   alarm->link = next;
   *last = alarm;
   break;
  }

  last = &next->link;
  next = next->link;
 }

 if(next == NULL){
  *last = alarm;
  alarm->link = NULL;
 }

 /*for test: output the list*/
 printf("[list: ");
 for(next = alarm_list; next != NULL; next = next->link)
 {
  printf("%d(%d)[/"%s/"]  ", next->time, next->time-time(NULL), next->message);
 }
 printf("]/n");

 if(current_alarm ==0  || alarm->time < current_alarm)
 {
  current_alarm = alarm->time;
  status = pthread_cond_signal(&alarm_cond);
  if(status != 0)
   ERROR_ABORT(status,"Signal cond");
 }

}

void* alarm_thread(void* arg)
{
 alarm_t* alarm;
 int sleep_time;
 time_t now;
 int status, expired;
 struct timespec cond_time;

 while(1)
 {
  status = pthread_mutex_lock(&alarm_mutex);
  if(status != 0)
   ERROR_ABORT(status, "lock");

  current_alarm = 0;

  while(alarm_list == NULL)
  {
   status = pthread_cond_wait(&alarm_cond, &alarm_mutex);
   if(status != 0 )
    ERROR_ABORT(status, "Wait cond");
  }

  alarm = alarm_list;
  alarm_list = alarm->link;
  now = time(NULL);
  expired = 0;

  if(alarm->time > now)
  {
   printf("[wating: %d(%d)/"%s/"]/n", alarm->time, alarm->time - time(NULL), alarm->message);

   cond_time.tv_sec = alarm->time;
   cond_time.tv_nsec = 0;
   current_alarm = alarm->time;
   while(current_alarm == alarm->time)
   {
    status = pthread_cond_timedwait(&alarm_cond, &alarm_mutex,&cond_time);
    if(status == ETIMEDOUT)
    {
     expired = 1;
     break;
    }
   }

   if(!expired)
    alarm_insert(alarm);
  }else
   expired = 1;

  if(expired)
  {
   printf("(%d) %s/n", alarm->seconds, alarm->message);
   free(alarm);
  }

  status = pthread_mutex_unlock(&alarm_mutex);
  if(status != 0)
   ERROR_ABORT(status, "Unlock mutex");
 }

 return 0;
}

int main()
{
 pthread_t pid;
 int status;
 char line[128];

 status = pthread_create(&pid, NULL, alarm_thread, NULL);
 if(status != 0)
  ERROR_ABORT(status, "pthread_create");

 while(1)
 {
  fprintf(stdout, "Alarm>");
  fgets(line, sizeof(line), stdin);
  if(strlen(line) <= 0)
   continue;

  alarm_t* alarm = (alarm_t*)malloc(sizeof(alarm_t));
  if(alarm == NULL)
   ERROR_ABORT(errno,"memory can't allocated!");

  if(sscanf(line, "%d %s", &alarm->seconds, alarm->message) != 2)
  {
   printf("Bad Command/n");
   free(alarm);
   continue;
  }

  status = pthread_mutex_lock(&alarm_mutex);
  if(status != 0)
   ERROR_ABORT(status, "pthread mutex locking..");

  alarm->time = time(NULL) + alarm->seconds;

  /* insert into list*/

  alarm_insert(alarm);

  status = pthread_mutex_unlock(&alarm_mutex);
  if(status != 0)
   ERROR_ABORT(status, "pthread mutex unlocking...");
 }

 return 0;
}
 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/future_fighter/archive/2009/02/12/3883415.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值