pthread_cond_wait()的使用方法


http://weihe6666.iteye.com/blog/1170141

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。 

一 pthread_cond_wait定义: 

函数原型:int   pthread_cond_wait(pthread_cond_t   *cond,   pthread_mutex_t   *mutex)   

参数: cond 条件变量  mutex 互斥锁 
第一个参数*cond是指向一个条件变量的指针。第二个参数*mutex则是对相关的互斥锁的指针。 

二 pthread_cond_wait示例理解 

pthread_cond_wait的机制比较难里理解,是条件变量中重要的成分。条件变量用于线程间同步,那么pthread_cond_wait必须和互斥锁同时作用在一个线程里,它同时起到对资源的加锁和解锁,看下面的示例: 


程序创建了2个新线程使他们同步运行,实现进程t_b打印9以内3的倍数,t_a打印其他的数,程序开始线程t_b不满足条件等待,线程t_a运行使a循环加1并打印。直到i为3的倍数时,线程t_a发送信号通知进程t_b,这时t_b满足条件,打印i值。 

C++代码   收藏代码
  1. 1 #include<pthread.h>  
  2.   2 #include<unistd.h>  
  3.   3 #include<stdio.h>  
  4.   4 #include<stdlib.h>  
  5.   5   
  6.   6 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/  
  7.   7 pthread_cond_t  cond = PTHREAD_COND_INITIALIZER;//init cond  
  8.   8   
  9.   9 void *thread1(void*);  
  10.  10 void *thread2(void*);  
  11.  11   
  12.  12 int i = 1; //global  
  13.  13   
  14.  14 int main(void){  
  15.  15     pthread_t t_a;  
  16.  16     pthread_t t_b;//two thread  
  17.  17   
  18.  18     pthread_create(&t_a,NULL,thread2,(void*)NULL);  
  19.  19     pthread_create(&t_b,NULL,thread1,(void*)NULL);//Create thread  
  20.  20   
  21.  21     pthread_join(t_b,NULL);//wait a_b thread end  
  22.  22     pthread_mutex_destroy(&mutex);  
  23.  23     pthread_cond_destroy(&cond);  
  24. 24     exit(0);  
  25.  25 }  
  26.  26   
  27.  27 void *thread1(void *junk){  
  28.  28     for(i = 1;i<= 9; i++){  
  29.  29         pthread_mutex_lock(&mutex); //互斥锁  
  30.  30         printf("call thread1 \n");  
  31.  31         if(i%3 == 0)  
  32.  32             pthread_cond_signal(&cond); //send sianal to t_b  
  33.  33         else  
  34.  34             printf("thread1: %d\n",i);  
  35.  35         pthread_mutex_unlock(&mutex);  
  36.  36         sleep(1);  
  37.  37     }  
  38.  38 }  
  39.  39   
  40.  40 void *thread2(void*junk){  
  41.  41     while(i < 9)  
  42.  42     {  
  43.  43         pthread_mutex_lock(&mutex);  
  44.  44         printf("call thread2 \n");  
  45.  45         if(i%3 != 0)  
  46.  46             pthread_cond_wait(&cond,&mutex); //wait  
  47.   47         printf("thread2: %d\n",i);  
  48.  48         pthread_mutex_unlock(&mutex);  
  49.  49         sleep(1);  
  50.  50     }  
  51.  51 }                                      

输出: 
call thread2 
call thread1 
thread1: 1 
call thread1 
thread1: 2 
call thread1 
thread2: 3 
call thread1 
thread1: 4 
call thread2 
call thread1 
thread1: 5 
call thread1 
thread2: 6 
call thread1 
thread1: 7 
call thread2 
call thread1 
thread1: 8 
call thread1 
thread2: 9 

示例的解释: 
call thread2:是线程2即t_b首先上锁,即 pthread_mutex_lock(&mutex);锁住了mutex使得此进程执行线程2中的临界区的代码,当执行到45行:if(i%3 != 0),此时i=1,满足此条件,则执行46行: pthread_cond_wait(&cond,&mutex); 这句是关键,pthread_cond_wait(&cond,&mutex)操作有两步,是原子操作:第一 解锁,先解除之前的pthread_mutex_lock锁定的mutex;第二 挂起,阻塞并在等待对列里休眠,即线程2挂起,直到再次被唤醒,唤醒的条件是由pthread_cond_signal(&cond);发出的cond信号来唤醒。 

call thread1:由于pthread_cond_wait已经对线程2解锁,此时另外的线程只有线程1,那么线程1对mutex上锁,若这时有多个线程,那么线程间上锁的顺序和操作系统有关。 

thread1: 1:线程1上锁后执行临界区的代码,当执行到if(i%3 == 0)此时i=1,不满足条件,则pthread_cond_signal(&cond);不被执行,那么线程2仍处于挂起状态,输出thread1: 1后线程1由pthread_mutex_unlock(&mutex);解锁。 

thread1: 2:这时此进程中只有2个线程,线程2处于挂起状态,那么只有线程1,则线程1又对mutex上锁,此时同样执行临界区的代码,而且i=2,不满足条件,pthread_cond_signal(&cond);不被执行,那么线程2仍处于挂起状态,输出thread1: 1后线程1由pthread_mutex_unlock(&mutex);解锁。 

call thread1:同样由线程1上锁,但此时i=3,满足条件pthread_cond_signal(&cond)被执行,那么pthread_cond_signal(&cond)会发出信号,来唤醒处于挂起的线程2。pthread_cond_signal同样由两个原子操作:1,解锁;2,发送信号;解锁即对线程1解锁,解除对mutex的上锁。发送信号,即给等待signal挂起的线程2发送信号,唤醒挂起的线程2。 

thread2: 3:由于pthread_cond_signal唤醒了线程2,即i=3满足条件,pthread_cond_wait(&cond,&mutex);被执行,那么pthread_cond_wait(&cond,&mutex)此时也有一步操作:上锁;即对线程2上锁,此时的pthread_cond_wait(&cond,&mutex)的操作相当与pthread_mutex_lock(&mutex);那么线程2继续执行上锁后的临界区的代码,并由pthread_mutex_unlock(&mutex);对线程2进行解锁。 

剩下的输出原理和上面解释的一样。 


纵观pthread_cond_wait,它的理解不可之把它看作一个简单的wait函数,它里面应该是一族函数,不同的函数在不同的条件下执行,理解pthread_cond_wait的机制可以很好的学习条件变量。

原文:http://www.cnblogs.com/diyingyun/archive/2011/11/25/2263164.html

pthread_cond_wait()

/************pthread_cond_wait()的使用方法**********/

    pthread_mutex_lock(&qlock);    

    pthread_cond_wait(&qready, &qlock);

    pthread_mutex_unlock(&qlock);

/*****************************************************/

 

The mutex passed to pthread_cond_wait protects the condition.The caller passes it locked to the function, which then atomically places them calling thread on the list of threads waiting for the condition and unlocks the mutex. This closes the window between the time that the condition is checked and the time that the thread goes to sleep waiting for the condition to change, so that the thread doesn't miss a change in the condition. When pthread_cond_wait returns, the mutex is again locked.

上面是APUE的原话,就是说pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,如果条件不成立我们就进入阻塞,但是进入阻塞这个期间,如果条件变量改变了的话,那我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁,使得其它线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又自动给mutex加锁。

实际上边代码的加解锁过程如下:

/************pthread_cond_wait()的使用方法**********/

pthread_mutex_lock(&qlock);    /*lock*/

pthread_cond_wait(&qready, &qlock); /*block-->unlock-->wait() return-->lock*/

pthread_mutex_unlock(&qlock); /*unlock*/

/*****************************************************/

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>

void* testThreadPool(int *t);
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITIALIZER;
int a = 0;

int main() {

int sock_fd, conn_fd;
int optval;

socklen_t cli_len;
struct sockaddr_in cli_addr, serv_addr;


sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0) {
   printf("socket\n");
}

optval = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval,
    sizeof(int)) < 0) {
   printf("setsockopt\n");
}

memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(4507);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(sock_fd, (struct sockaddr *) &serv_addr,
    sizeof(struct sockaddr_in)) < 0) {
   printf("bind\n");
}

if (listen(sock_fd, 100) < 0) {
   printf("listen\n");
}

cli_len = sizeof(struct sockaddr_in);
int t;
pthread_t * mythread;
mythread = (pthread_t*) malloc(100 * sizeof(pthread_t));
for (t = 0; t < 5; t++) {
   int *i=(int*)malloc(sizeof(int));
   *i=t;
   if (pthread_create(&mythread[t], NULL, (void*)testThreadPool, (void*)i) != 0) {
    printf("pthread_create");
   }
}

while (1) {
   conn_fd = accept(sock_fd, (struct sockaddr *) &cli_addr, &cli_len);
   if (conn_fd < 0) {
    printf("accept\n");
   }
   printf("accept a new client, ip:%s\n", inet_ntoa(cli_addr.sin_addr));

   pthread_mutex_lock(&clifd_mutex);
   a=conn_fd;
   pthread_cond_signal(&clifd_cond);
   pthread_mutex_unlock(&clifd_mutex);
}
return 0;
}

void* testThreadPool(int *t) {

printf("t is %d\n", *t);
for (;;) {
   pthread_mutex_lock(&clifd_mutex);
   pthread_cond_wait(&clifd_cond, &clifd_mutex);
   printf("a is %d\n", a);
   printf("t is %d\n", *t);
   pthread_mutex_unlock(&clifd_mutex);
   sleep(100);
}
return (void*) 0;
}

了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。 

首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象,然而该列表恰巧是空的。这一特定线程什么也干不了 -- 其设计意图是从列表中除去节点,但是现在却没有节点。因此,它只能: 

锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。 

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的信号时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。 【要求解锁并阻塞是一个原子操作】

此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。 

现在继续说明,假设另一个线程(称作“2 号线程)锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒。 

现在,看一下第一个线程发生了什么。您可能会认为在 号线程调用 pthread_cond_broadcast(&mymutex) 之后,号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。 

停止并回顾! 
那个过程非常复杂,因此让我们先来回顾一下。第一个线程首先调用: 
pthread_mutex_lock(&mymutex); 
然后,它检查了列表。没有找到感兴趣的东西,于是它调用:
pthread_cond_wait(&mycond, &mymutex); 
 
然后,pthread_cond_wait() 调用在返回前执行许多操作: 
 
pthread_mutex_unlock(&mymutex); 
 
它对 mymutex 解锁,然后进入睡眠状态,等待 mycond 以接收 POSIX 线程信号。一旦接收到信号(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会苏醒。但 pthread_cond_wait() 没有立即返回 -- 它还要做一件事:重新锁定 mutex
pthread_mutex_lock(&mymutex); 
 
pthread_cond_wait() 知道我们在查找 mymutex “背后的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。


`pthread_cond_timedwait` 函数用于在指定的时间内等待条件变量满足。以下是 `pthread_cond_timedwait` 函数的使用方法: ```c int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); ``` 参数说明: - `cond`:指向条件变量的指针。 - `mutex`:指向互斥锁的指针,用于保护共享资源。 - `abstime`:指向 `struct timespec` 结构体的指针,表示等待的绝对时间。 函数返回值: - 成功时返回 0。 - 如果等待超时,则返回 ETIMEDOUT 错误。 - 其他错误情况下返回相应的错误码。 使用 `pthread_cond_timedwait` 函数的一般步骤如下: 1. 在进入等待之前,获取互斥锁。 2. 设置等待的绝对时间。 3. 调用 `pthread_cond_timedwait` 函数,传入条件变量、互斥锁和绝对时间作为参数。 4. 根据返回值判断等待的结果,如果返回 0,则条件满足可以继续执行;如果返回 ETIMEDOUT,则表示等待超时。 5. 在条件满足或超时后,释放互斥锁。 下面是一个示例代码: ```c #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; void* thread_func(void* arg) { sleep(5); // 模拟一些操作 pthread_mutex_lock(&mutex); pthread_cond_signal(&cond); // 发送信号通知等待的线程 pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t thread; struct timespec timeout; // 获取当前时间并设置等待时间为 3 秒 clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec += 3; pthread_create(&thread, NULL, thread_func, NULL); pthread_mutex_lock(&mutex); int result = pthread_cond_timedwait(&cond, &mutex, &timeout); if (result == 0) { printf("条件满足,继续执行\n"); } else if (result == ETIMEDOUT) { printf("等待超时\n"); } else { printf("等待出错\n"); } pthread_mutex_unlock(&mutex); pthread_join(thread, NULL); return 0; } ``` 在上述示例中,我们创建了一个线程,在 5 秒后发送信号通知等待的线程。等待的线程在等待超过 3 秒后会自动返回 ETIMEDOUT 错误。 希望以上信息能够帮助到你。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值