条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待条件变量的条件成立而挂起;另一个线程使条件成立(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
1.1、 创建和注销
条件变量和互斥锁一样,都有静态、动态两种创建方式:
静态方式:PTHREAD_COND_INITIALIZER 常量,如下:(不常用)
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态方式:pthread_cond_init() 函数,API定义如下:(常用)
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr); //尽管POSIX标准中为条件变量定义了属性,但在Linux Threads中没有实现,因此cond_attr值通常为NULL,且被忽略。注销一个条件变量需要调用 pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_init.c | |
#include<pthread.h>
#include<stdio.h>
int main()
{
pthread_cond_t cond;
int ret;
ret=pthread_cond_init(&cond,NULL);
if(ret!=0)
{
printf("error init\n");
return -1;
}
ret=pthread_cond_destroy(&cond);
if(ret!=0)
{
printf("error destroy\n");
return -1;
}
return 0;
}
|
1、修改cond队列,我要等在该条件变量上
2、解锁mutex
3、放弃cpu,睡觉
线程醒来
1、重新对mutex加锁
|
等待条件有两种方式:无条件等待 pthread_cond_wait() 和计时等待 pthread_cond_timedwait()
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int
pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const
struct timespec *abstime); //最后面的时间是一个绝对时间
线程解开mutex指向的锁并被条件变量cond阻塞。其中计时等待方式表示经历abstime段时间后,即使条件变量不满足,阻塞也被解除。无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。(也就是说在做pthread_cond_wait之前,往往要用pthread_mutex_lock进行加锁,而调用pthread_cond_wait函数会将锁解开,然后将线程挂起阻塞。直到条件被pthread_cond_signal激发,再将锁状态恢复为锁定状态,最后再用pthread_mutex_unlock进行解锁)。
激发条件有两种形式:
int pthread_cond_signal(pthread_cond_t *cond,); \\激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;
int pthread_cond_broadcast(pthread_cond_t *cond,); \\激活所有等待线程;
加锁
-------------------------------------------------------------------------------
wait:
1、修改cond队列,我要等在该条件变量上
2、解锁mutex
3、放弃cpu,睡觉
线程醒来
1、重新对mutex加锁,激活一个线程
-------------------------------------------------------------------------------
解锁
|
pthread_mutex_lock(&p1->mutex); pthread_cond_wait(&p1->cond,&p1->mutex); pthread_mutex_unlock(&p1->mutex); |
****(关于pthread_cleanup_pusp()/pop()函数出现的位置)pthread_cond_wait()和pthread_cond_timedwait()都被实现为取消点,也就是说如果pthread_cond_wait()被取消,则退出阻塞,然后将锁状态恢复,则此时mutex是保持锁定状态的,而当前线程已经被取消掉,那么解锁的操作就会得不到执行,此时锁得不到释放,就会造成死锁,因而需要定义退出回调函数来为其解锁。
pthread_cond_wait.c | pthread_cond_timedwait.c |
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
typedef struct
{
pthread_cond_t cond;
pthread_mutex_t mutex;
}cm,*pcm;
void* thread(void* p)
{
pcm p1=(pcm)p; //将void型指针强转为结构体指针
int ret;
pthread_mutex_lock(&p1->mutex);
printf("I will wait\n");
ret=pthread_cond_wait(&p1->cond,&p1->mutex);
printf("ret=%d\n",ret);
pthread_mutex_unlock(&p1->mutex);
pthread_exit(NULL);
}
int main()
{
int ret;
cm p; //局部变量,里边有一个锁,一个条件变量
ret=pthread_cond_init(&p.cond,NULL);
if(ret!=0)
{
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=pthread_mutex_init(&p.mutex,NULL);
if(ret!=0)
{
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_t pth_id;
pthread_create(&pth_id,NULL,thread,(void*)&p);
sleep(1);
ret=pthread_cond_signal(&p.cond);
if(ret!=0)
{
printf("pthread_cond_signal failed,ret=%d\n",ret);
return -1;
}
printf("I am main thread,I wait child\n");
pthread_join(pth_id,NULL);
ret=pthread_cond_destroy(&p.cond);
if(ret!=0)
{
printf("pthread_cond_destroy failed,ret=%d\n",ret);
return -1;
}
pthread_mutex_destroy(&p.cond);
return 0;
}
|
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
typedef struct
{
pthread_cond_t cond;
pthread_mutex_t mutex;
}cm,*pcm;
//子线程timedwait 等待条件成立,超时子线程自己醒来
void* thread(void* p)
{
pcm p1=(pcm)p; //将void型指针强转为结构体指针
int ret;
pthread_mutex_lock(&p1->mutex);
printf("I will wait\n");
struct timespec abstime;
memset(&abstime,0,sizeof(abstime));
abstime.tv_sec=time(NULL)+5; //绝对时间是指 当前时间+超时时间
ret=pthread_cond_timedwait(&p1->cond,&p1->mutex,&abstime);
printf("ret=%d\n",ret);
pthread_mutex_unlock(&p1->mutex);
pthread_exit(NULL);
}
int main()
{
int ret;
cm p; //局部变量,里边有一个锁,一个条件变量
ret=pthread_cond_init(&p.cond,NULL);
if(ret!=0)
{
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=pthread_mutex_init(&p.mutex,NULL);
if(ret!=0)
{
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_t pth_id;
pthread_create(&pth_id,NULL,thread,(void*)&p);
//sleep(1);
//ret=pthread_cond_signal(&p.cond); //让线程自己到时间醒来
if(ret!=0)
{
printf("pthread_cond_signal failed,ret=%d\n",ret);
return -1;
}
pthread_join(pth_id,NULL);
printf("I am main thread,I wait child\n");
ret=pthread_cond_destroy(&p.cond);
pthread_mutex_destroy(&p.cond);
return 0;
}
//110是超时错误码
//没有超时,发信号唤醒
|
man 手册找不到 struct timespec 就在库文件里找一下 find /usr/include -name *.h | xargs grep timespec 在结果中找一个定义了所要找的结构体 |
broad_cond_broadcast.c | 这个是改进,main线程不睡眠等待。直接在线程里面确定好自己的标号 |
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
typedef struct
{
pthread_cond_t cond;
pthread_mutex_t mutex;
int i;
}cm,*pcm;
void* thread(void* p)
{
pcm p1=(pcm)p;
int ret;
int a=p1->i;
pthread_mutex_lock(&p1->mutex);
printf("thread %d will wait\n",a);
ret=pthread_cond_wait(&p1->cond,&p1->mutex);
printf("thread %d wake\n",a);
pthread_mutex_unlock(&p1->mutex);
pthread_exit((void*)a);
}
int main()
{
int ret;
cm p; //局部变量,里边有一个锁,一个条件变量
ret=pthread_cond_init(&p.cond,NULL);
if(ret!=0)
{
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=pthread_mutex_init(&p.mutex,NULL);
if(ret!=0)
{
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_t pth_id[5];
int i;
for(i=0;i<5;i++)
{
p.i=i+1;
pthread_create(&pth_id[i],NULL,thread,(void*)&p); //创建子线程的时候main线程执行到这里不会亲自去执行创建过程,而是交给内核去做,main线程接着做下面的事,由于main执行这个for循环很快,多以在第一个线程创建好的时候 p.i 已经加到5了,所以最后打印的全部是5,要想一次打印就等在后面让main线程暂停会,等线程创建好开始执行自己的代码的时候再进行i++创建下一个线程;这样,子线程中a拿到的就是我们想要的序号。
sleep(1); //①
}
ret=pthread_cond_broadcast(&p.cond);
sleep(2); //②,给挂起子线程时间获取锁资源,打印信息;
if(ret!=0)
{
printf("pthread_cond_signal failed,ret=%d\n",ret);
return -1;
}
printf("I am main thread,I wait child\n");
int val;
for(i=0;i<5;i++)
{
pthread_join(pth_id[i],(void**)&val);
printf("exit thread is %d \n",(int)val);
}
pthread_cond_destroy(&p.cond);
pthread_mutex_destroy(&p.mutex);
return 0;
}
|
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
typedef struct
{
pthread_cond_t cond;
pthread_mutex_t mutex;
int i;
}cm,*pcm;
int j=0;
void* thread(void* p)
{
pcm p1=(pcm)p;
int ret;
// int a=p1->i;
j++;
int a=j;
pthread_mutex_lock(&p1->mutex);
printf("thread %d will wait\n",a);
ret=pthread_cond_wait(&p1->cond,&p1->mutex);
printf("thread %d wake\n",a);
pthread_mutex_unlock(&p1->mutex);
pthread_exit(NULL);
}
int main()
{
int ret;
cm p;
ret=pthread_cond_init(&p.cond,NULL);
if(ret!=0)
{
printf("pthread_cond_init failed,ret=%d\n",ret);
return -1;
}
ret=pthread_mutex_init(&p.mutex,NULL);
if(ret!=0)
{
printf("pthread_mutex_init failed,ret=%d\n",ret);
return -1;
}
pthread_t pth_id[5];
int i;
for(i=0;i<5;i++)
{
p.i=i+1;
pthread_create(&pth_id[i],NULL,thread,(void*)&p);
}
sleep(1);
ret=pthread_cond_broadcast(&p.cond);
if(ret!=0)
{
printf("pthread_cond_signal failed,ret=%d\n",ret);
return -1;
}
printf("I am main thread,I wait child\n");
for(i=0;i<5;i++)
{
pthread_join(pth_id[i],NULL);
}
ret=pthread_cond_destroy(&p.cond);
if(ret!=0)
{
printf("pthread_cond_destroy failed,ret=%d\n",ret);
return -1;
}
return 0;
}
|