正在加载……
专注、离线、切勿分心
1、条件变量

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


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加锁



2、等待和激发

 等待条件有两种方式无条件等待 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;
}
//  main线程执行速度快于子线程,等线程创建好,p.i 已经加到5了
             


//  不加sleep等待全部线程都唤醒释放锁,因为第一个线程最先挂起,所以第一个打印信息,并且释放锁,在main线程回收第一个线程资源的的时候,后面的4个线程拿到锁,唤醒、打印信息退出,后面main线程就可以一次回收每个线程,打印退出值。









posted on 2018-03-08 09:33  正在加载……  阅读(255)  评论(0编辑  收藏  举报