linux条件变量实现线程同步

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/andrewgithub/article/details/82527785
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);     
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);  
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);  

条件变量用于线程之间的通信,和互斥锁一起使用。条件变量用于及时通知等待的线程条件的变化,使线程不至于错过变化

具体的使用方式在实际的例子中进行说明;

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <unistd.h>

typedef struct{
    int res;
    int is_wait;   //用户给出用于判断的条件
    pthread_cond_t cond;   //条件变量
    pthread_mutex_t mutex; //互斥锁
}Result;



//将计算结果放置在Result中的线程运行函数
void* set_fn(void *arg)
{
    int i = 1, sum = 0;

    for(;i <= 100; i++)
    {
        sum += i;
    }

    Result *r = (Result *)arg;
    //将计算结果放置到Result的rest中
    r->res = sum;
    pthread_mutex_lock(&r->mutex);
    //接着判断获取结果的线程是否已经准备好
    //在while循环中进行加锁和释放锁看起来有点怪,这是因为while是一个循环造成的,也必须这样写
    //才能保证while循环中对while执行的(!r->is_wait)共享资源的保护
    while(!r->is_wait)
    {
        //若是没有准备好就在这里进行锁的释放,否则会造成死锁
        //以为一旦该函数先进入而且不释放锁那么下一个函数无法获取资源也无法改变准备状态
        pthread_mutex_unlock(&r->mutex);
        //获取结果的线程还没有准备好
        usleep(100);
        //在新一个循环开始的时候可以保护,while循环中对共享资源的稳定的使用, r->is_wait
        pthread_mutex_lock(&r->mutex);
    }    
    pthread_mutex_unlock(&r->mutex);
    
    //唤醒等待该资源的线程,执行之后等待该资源的线程就可以接着向pthread_cond_wait下面走
    pthread_cond_broadcast(&r->cond);


    return (void*)0;
}

//获取结果的线程运行函数
void* get_fn(void *arg)
{
    Result *r = (Result *)arg;
    //操作两个线程否能够获取的数据需要进行加锁之后在进行操作
    //两个线程对判断条件的判断是互斥的
    pthread_mutex_lock(&r->mutex);
    //设置为1代表获取结果的线程已经准备好
    r->is_wait = 1;

    //获取结果的线程等待,等待用于计算的线程去唤醒
    //计算的线程执行,pthread_cond_broadcast唤醒操作之后该函数才会取消阻塞
    pthread_cond_wait(&r->cond, &r->mutex);
    //被唤醒之后,解锁资源
    pthread_mutex_unlock(&r->mutex);

    //去获取结算结果
    int res = r->res;
    //pthread_self是一种函数,功能是获得线程自身的ID
    printf("0x%lx get sum is %d\n", pthread_self(), res);


    return (void *)0;
}


int main(void)
{
    int err;
    pthread_t cal, get;

    Result r;
    r.is_wait =0;
    //初始化条件变量和互斥锁
    /* 初始化条件变量 */
    pthread_cond_init(&r.cond, NULL);
    /* 初始化互斥锁 */
    pthread_mutex_init(&r.mutex, NULL);


    /* 创建线程去使用条件变量和互斥锁 */
    if((err = pthread_create(&get, NULL, get_fn, (void*)&r)) != 0)
    {
        perror("pthread create error!");
    }
    if((err = pthread_create(&cal, NULL, set_fn, (void*)&r)) != 0)
    {
        perror("pthread create error!");
    }


    /* 主线程等待销毁创建的子线程 */
    pthread_join(cal, NULL);
    pthread_join(get, NULL);

    /* 销毁创建的条件变量和互斥变量 */
    pthread_cond_destroy(&r.cond);
    pthread_mutex_destroy(&r.mutex);



    return 0;
}

pthread_cond_wait函数的内部流程:

pthread_cond_wait

{

    ulock(&mutex);//释放锁

   lock(&mutex);

//将线程自己插入到条件变量的等待队列中去

//等待的线程阻塞 < == 等其他线程通知唤醒(broadcast/signal)

唤醒之后,调用lock(&mutex)

从等待队列中删除线程自己

}

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <unistd.h>
/*
*一个线程负责计算结果,多个线程负责获取结果
*/


typedef struct{
    int res;
    int counter;   //用于统计获取结果线程的数量
    pthread_cond_t cond;   //条件变量
    pthread_mutex_t mutex; //互斥锁
}Result;



//将计算结果放置在Result中的线程运行函数
void* set_fn(void *arg)
{
    int i = 1, sum = 0;

    for(;i <= 100; i++)
    {
        sum += i;
    }

    Result *r = (Result *)arg;
    //将计算结果放置到Result的rest中
    r->res = sum;
    pthread_mutex_lock(&r->mutex);
    //接着判断获取结果的线程是否已经准备好
    //在while循环中进行加锁和释放锁看起来有点怪,这是因为while是一个循环造成的,也必须这样写
    //判断获取结果的线程是否达到指定的数量
    while(r->counter < 2)
    {
        //若是没有准备好就在这里进行锁的释放,否则会造成死锁
        //以为一旦该函数先进入而且不释放锁那么下一个函数无法获取资源也无法改变准备状态
        pthread_mutex_unlock(&r->mutex);
        //获取结果的线程还没有准备好
        usleep(100);
        //在新一个循环开始的时候可以保护,while循环中对共享资源的稳定的使用, r->is_wait
        pthread_mutex_lock(&r->mutex);
    }    
    pthread_mutex_unlock(&r->mutex);
    
    //唤醒等待该资源的线程,执行之后等待该资源的线程就可以接着向pthread_cond_wait下面走
    pthread_cond_broadcast(&r->cond);


    return (void*)0;
}

//获取结果的线程运行函数
void* get_fn(void *arg)
{
    Result *r = (Result *)arg;
    //操作两个线程否能够获取的数据需要进行加锁之后在进行操作
    //两个线程对判断条件的判断是互斥的
    pthread_mutex_lock(&r->mutex);
    //设置为1代表获取结果的线程已经准备好
    r->counter++;

    //获取结果的线程等待,等待用于计算的线程去唤醒
    //计算的线程执行,pthread_cond_broadcast唤醒操作之后该函数才会取消阻塞
    pthread_cond_wait(&r->cond, &r->mutex);
    //被唤醒之后,解锁资源
    pthread_mutex_unlock(&r->mutex);

    //去获取结算结果
    int res = r->res;
    //pthread_self是一种函数,功能是获得线程自身的ID
    printf("0x%lx get sum is %d\n", pthread_self(), res);


    return (void *)0;
}


int main(void)
{
    int err;
    pthread_t cal, get1, get2;

    Result r;
    r.counter = 0;
    //初始化条件变量和互斥锁
    /* 初始化条件变量 */
    pthread_cond_init(&r.cond, NULL);
    /* 初始化互斥锁 */
    pthread_mutex_init(&r.mutex, NULL);


    /* 创建线程去使用条件变量和互斥锁 */
    if((err = pthread_create(&get1, NULL, get_fn, (void*)&r)) != 0)
    {
        perror("pthread create error!");
    }

     if((err = pthread_create(&get2, NULL, get_fn, (void*)&r)) != 0)
    {
        perror("pthread create error!");
    }


    if((err = pthread_create(&cal, NULL, set_fn, (void*)&r)) != 0)
    {
        perror("pthread create error!");
    }


    /* 主线程等待销毁创建的子线程 */
    pthread_join(cal, NULL);
    pthread_join(get1, NULL);
     pthread_join(get2, NULL);

    /* 销毁创建的条件变量和互斥变量 */
    pthread_cond_destroy(&r.cond);
    pthread_mutex_destroy(&r.mutex);



    return 0;
}
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>

/*
 *在子线程中调用alarm()函数产生的信号是发送给主控线程的
 *而不是子线程
 */

/*执行结果分析
 *andrew@andrew-Thurley:~/work/pthread$ ./a.out
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 1
(7f7b13dcb700) i: 2
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 


//应该是等10s,主线程才会再次输出这句话,但是在这里就输出了
//原因是: 1. sleep会被  alarm产生的信号打断 
//       2. 子线程产生的闹钟信号是被主线程不捕获的 
//要想被子线程捕获到,需要使用屏蔽信号将主线程对alarm信号的捕获屏蔽掉


(7f7b13dcb700) i: 3
(7f7b13dcb700) i: 4
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 5
(7f7b13dcb700) i: 6
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 7
(7f7b13dcb700) i: 8
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 9
(7f7b13dcb700) i: 10
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 11
(7f7b13dcb700) i: 12
pthread id is the sig_handler: 7f7b145b8700
 * */

void sig_handler(int signo)
{
    //处理完信号之后就在次的设置信号处理函数这样就能周期的进行信号处理了
    printf("pthread id is the sig_handler: %lx\n", pthread_self());
    if(signo == SIGALRM)
    {
        printf("time out!\n");
    }
    alarm(2);

}

void * th_fn(void *arg)
{
    if(signal(SIGALRM, sig_handler) == SIG_ERR)
    {
        perror("signal sigalrm error!");
    }
    //在子线程中设置定时器,时间两秒
    alarm(2);
    
    int i;
    for(i =1 ; i < 100; i++)
    {
        printf("(%lx) i: %d\n", pthread_self(), i);
        sleep(1);
    }
    return (void *)0;
}

int err;
int main(void)
{
    pthread_t th;
    //以分离状态启动一个线程
    //定义一个线程属性
    pthread_attr_t attr;
    //初始化分离状态属性
    pthread_attr_init(&attr);
    //设置分离状态属性
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    //初始化子线程,以分离状态启动子线程
    if(( err = pthread_create(&th, &attr, th_fn, (void *)0)) != 0 )
    {
        perror("pthread create error !");

    }


    while(1)
    {
        printf("control thread(%lx) is running \n", pthread_self());

        sleep(10);
    }

    printf("control thread is over!\n");


    return 0;
}











  
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>

/*
 *在子线程中调用alarm()函数产生的信号是发送给主控线程的
 *而不是子线程
 */

/*执行结果分析
 *andrew@andrew-Thurley:~/work/pthread$ ./a.out
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 1
(7f7b13dcb700) i: 2
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 


//应该是等10s,主线程才会再次输出这句话,但是在这里就输出了
//原因是: 1. sleep会被  alarm产生的信号打断 
//       2. 子线程产生的闹钟信号是被主线程不捕获的 



(7f7b13dcb700) i: 3
(7f7b13dcb700) i: 4
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 5
(7f7b13dcb700) i: 6
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 7
(7f7b13dcb700) i: 8
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 9
(7f7b13dcb700) i: 10
pthread id is the sig_handler: 7f7b145b8700
time out!
control thread(7f7b145b8700) is running 
(7f7b13dcb700) i: 11
(7f7b13dcb700) i: 12
pthread id is the sig_handler: 7f7b145b8700
 * */

void sig_handler(int signo)
{
    //处理完信号之后就在次的设置信号处理函数这样就能周期的进行信号处理了
    printf("pthread id is the sig_handler: %lx\n", pthread_self());
    if(signo == SIGALRM)
    {
        printf("time out!\n");
    }
    alarm(2);

}

void * th_fn(void *arg)
{
    if(signal(SIGALRM, sig_handler) == SIG_ERR)
    {
        perror("signal sigalrm error!");
    }
    //在子线程中设置定时器,时间两秒
    alarm(2);
    
    int i;
    for(i =1 ; i < 100; i++)
    {
        printf("(%lx) i: %d\n", pthread_self(), i);
        sleep(1);
    }
    return (void *)0;
}

int err;
int main(void)
{
    pthread_t th;
    //以分离状态启动一个线程
    //定义一个线程属性
    pthread_attr_t attr;
    //初始化分离状态属性
    pthread_attr_init(&attr);
    //设置分离状态属性
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    //初始化子线程,以分离状态启动子线程
    if(( err = pthread_create(&th, &attr, th_fn, (void *)0)) != 0 )
    {
        perror("pthread create error !");

    }

    while(1)
    {
        printf("control thread(%lx) is running \n", pthread_self());

        sleep(10);
    }

    printf("control thread is over!\n");


    return 0;
}











  
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <unistd.h>
#include <semaphore.h>


/* 
 *函数说明:
 * 通过线程信号量控制线程的先后执行顺序
 * 之后线程c先运行之后才会进行线程b的运行,只有线程b运行之后才会进行线程a的运行
 * 用到的函数  sem_wait   sem_init  sem_post
 *  */




//定义线程信号量
sem_t sem1;
sem_t sem2;




void* a_fn(void *arg)
{


    sem_wait(&sem1);

    printf("thread a is running!\n");
    return (void *)0;
}
void* b_fn(void *arg)
{

    sem_wait(&sem2);
    printf("thread b is running!\n");
    //释放线程a
    sem_post(&sem1);

    return (void *)0;
}
void* c_fn(void *arg)
{

    printf("thread c is running!\n");
    //释放线程b      sem2 + 1
    sem_post(&sem2);
    return (void *)0;
}

/*
 *       #include <semaphore.h>
 *
 *      int sem_init(sem_t *sem, int pshared, unsigned int value);
 *                pshared:0共享  1不共享
 *                value:线程信号量初始值
*/


int main(void)
{
    pthread_t a, b, c;
    //线程信号量初始化,初始值为0
    sem_init(&sem1, 0, 0);
    sem_init(&sem2, 0, 0);

    pthread_create(&a, NULL, a_fn, (void *)0);
    pthread_create(&b, NULL, b_fn, (void *)0);
    pthread_create(&c, NULL, c_fn, (void *)0);

    //主函数中要等待销毁线程

    pthread_join(a, NULL);
    pthread_join(b, NULL);
    pthread_join(c, NULL);



    sem_destroy(&sem1);
    sem_destroy(&sem2);



    return 0;
}

/* 测试结果 */

/*
 *./a.out
 *   thread c is running!
 *   thread b is running!
 *   thread a is running!

 */



猜你喜欢

转载自blog.csdn.net/andrewgithub/article/details/82527785