互斥锁和条件变量

来源:互联网 发布:淘宝怎么开通旺铺 编辑:程序博客网 时间:2024/05/16 07:12

为了允许在线程或进程之间共享数据,同步是必须的,互斥锁和条件变量是同步的基本组成部分.

1.互斥锁

互斥锁是用来保护临界区资源,实际上保护的是临界区中被操纵的数据,互斥锁通常用于保护由多个线程或进程分享的共享数据,一般是一些可供线程间使用的全局变量,来达到线程同步的目的,保证任何时刻只有一个线程或进程在执行其中的代码,  

互斥锁的函数:  

1.pthread_mutex_init(pthread_mutex_t  *mutex,const pthread_mutexattr_t   *mutexattr)函数   初始化一个互斥锁 使用互斥锁之前必须进行初始化操作,初始化有两种方法,1.静态赋值法.对于静态分配的互斥锁一般用宏赋值的方式初始化eg:static  pthread_mutex_t mutex = PTHREAD_NUTEX_INITALIZER;2.对于动态分配的互斥锁(如调用malloc)或分配在共享内存中,则必须调用int    pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t  *mutexattr)初始化;  

函数中的mutexattr是锁的属性:  

属性值:PTHREAD_MUTEX_TIMED_NP    普通锁:当一个线程加锁后,其余请求的线程形成等待队列,解锁后按优先级获得锁; PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许一个线程队同一个锁多次加锁,并通过多次unlock 解锁,如果是不同线程请求,则在解锁时重新竞争; PTHREAD_MUTEX_ERRORCHECK_NP: 检错锁:在同一个线程请求同一个锁的情况下,返回EDEADLK,否则执行的动作和类型PTHREAD_MUTEX_TIMED_NP相同; PTHREAD_MUTEX_ADAPTIVE_NP 适应锁:解锁后重新竞争 2.int  pthread_mutex_destroy(pthread_mutex_t  *mutex)函数  注销一个互斥锁 清除一个互锁意味着释放它占用的资源,清除锁时要求当前处于开放状态,若锁处于锁定状态,函数返回EBUSY,该函数成功执行返回0. 3.int pthread_mutex_lock(pthread_mutex_t *mutex)函数    加锁,如果不成功,阻塞等待 用pthread_mutex_lock(pthread_mutex_t *mutex)函数加锁时,如果mutex 已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其它线程释放. 注意:加锁时,不论那种类型的锁,都不可能被两个不同的线程同时得到,其中一个必须等待解锁,在同一进程中的线程,如果加锁后没有解锁,则其它线程将无法在获得该锁. 4. int pthread_mutex_unlock(pthread_mutex_t *mutex);                 解锁; 用此函数有两个条件,1.互斥锁必须处于加锁状态,调用本函数的线程必须是给互斥锁加锁的线程; 5.pthread_mutex_trylock(pthread_mutex_t *mutex);                   测试锁,如果不成功则立即返回,错误代码为BEBUSY;

2.条件变量:

条件变量是利用线程间共享的全局变量进行同步的一种机制,条件变量宏观上类似于if  语句,符合条件就能执行某段程序,否则只能等待条件成立. 使用条件变量主要包括两个动作:一个等待使用资源的线程"条件变量被设置为真";  另一个线程在使用完资源后"设置条件为真",这样就能保证线程间的同步了,条件变量的操作函数: int pthread_cond_init ( pthread_cond_t  *cond,pthread_condattr_t  *cond_attr)函数: 其中,cond_attr参数是条件变量的属性,由于没有得到实现,所以它的值通常为NULL; 等待函数有两个: int pthread_cond_wait([pthread_cond_t *cond,pthread_mutex_t *mutex); 该函数释放由mutex指向的互斥锁,同时使当前线程关于cond指向的条件变量阻塞.直到条件信号唤醒,通常条件表达式在互斥锁的保护下求值,如果条件表达式为假,那么线程基于条件变量阻塞,当一个线程改变条件变量的值时,条件变量获得一个信号,使等待条件变量的线程退出阻塞状态. int  pthread_cond_timedwait函数和pthread_cond_wait函数用法类似,差别在于pthread_cond_timewait函数将阻塞直到条件变量获得信号或经过由 abstime指定的时间,也就是说,如果在给定时刻的条件前没有满足,就会ETIMEOUT,结束等待. 线程被条件变量阻塞后,可通过函数int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); pthread_cond_signal()激活一个等待条件成立的线程,存在多个等待线程时,按入队顺序激活其中一个,而pthread_cond_broadcast() 则激活所有等待的线程. 当一个条件变量不在使用时,需要将其清除,清除一个条件变量通过调用 int pthread_cond_destroy(pthread_cond_t  *cond)来清除,此函数清除由cond指向的条件变量,注意:只有在没有线程的等待的该条件的时候才能清除这个条件变量.否则返回EBUSY. 来看个例子:

#include <stdio.h>#include <stdlib.h>#include <pthread.h>void my_err(const char * err_string,int line){fprintf(stderr,"line:%d",line);perror(err_string);exit(1);}typedef struct private_tag{pthread_t thread_id;char *string;}private_t;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_key_t key;int count = 0;void *key_get(void){void *value = NULL;int status;value = pthread_getspecific(key);if(value == NULL){value=malloc(sizeof(pthread_key_t));if(value == NULL){my_err("malloc",__LINE__);}status = pthread_setspecific(key,value);if(status!=0){my_err("set",__LINE__);}       }return value;}void *thread_routine(void *arg){private_t value = NULL;value=(private_t *)key_get();value->thread_id=pthread_self();value->string=(char *)arg;printf("\"%s\" staring.....\n",value->string);sleep(2);}

结果:

yang@liu:~/syc/第八章$ ./mutex 条件变量学习!线程1正在运行....线程1得到了这个条件!线程2正在运行!线程2得到了这个条件!线程2正在运行!线程2得到了这个条件!线程2正在运行!线程2得到了这个条件!线程2正在运行!线程2得到了这个条件!线程1正在运行....线程1得到了这个条件!

上面只是运行结果的一部分,从结果可以看出,thread1和thread2通过条件变量同步运行,在线程thread1和thread2中可以看出,条件变量要配合互锁使用,这样可以防止多个线程同时请求pthread_cond_wait. 来看两个例子:实现生产者—消费者问题,先只考虑多个生产者线程之间的同步,直到所有的生产者线程都完成工作以后,才启动消费者线程

#include <stdio.h>#include <unistd.h>#include <pthread.h>pthread_mutex_t mutex;pthread_cond_t  cond;void *thread1(void *arg){pthread_cleanup_push(pthread_mutex_unlock,&mutex);while(1){printf("线程1正在运行....\n");pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);printf("线程1得到了这个条件!\n");pthread_mutex_unlock(&mutex);sleep(4);}pthread_cleanup_pop(0);}void *thread2(void *arg){while(1){printf("线程2正在运行!\n");pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);printf("线程2得到了这个条件!\n");pthread_mutex_unlock(&mutex);sleep(1);}}int main(void){pthread_t thid1,thid2;printf("条件变量学习!\n");pthread_mutex_init(&mutex,NULL);pthread_cond_init(&cond,NULL);pthread_create(&thid1,NULL,(void *)thread1,NULL);pthread_create(&thid2,NULL,(void *)thread2,NULL);do{pthread_cond_signal(&cond);  }while(1);sleep(50);pthread_exit(0);}

结果:

yang@liu:~/syc/第八章$ ./mutexcond  1000000 5生产者正在工作!生产者正在工作!生产者正在工作!生产者正在工作!生产者正在工作!count[0] = 206634count[1] = 209573count[2] = 188241count[3] = 197221count[4] = 198331消费者正在工作!

可以看出,生产者进程生产完,消费者进程才开始工作! 2.通过条件变量判断判断,当消费者等待生产者,当生产者有生产时;即唤醒消费者,消费者开始工作.代码如下:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#include <errno.h>#define     MAXNITEMS        1000000#define     MAXNTHREADS     100int nitems;struct{    pthread_mutex_t     mutex;    int                 buff[MAXNITEMS];    int                 nput;    int                 nval;} shared = {    PTHREAD_MUTEX_INITIALIZER};//条件变量struct {    pthread_mutex_t mutex;      pthread_cond_t  cond;    int nready;}nready = {  PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER};void *produce(void*);void *consume(void*);int main(int argc,char *argv[]){    int     i,nthreads,count[MAXNTHREADS];    pthread_t tid_produce[MAXNTHREADS],tid_consume;    if(argc != 3)    {        printf("usage: producongs2 <#itmes> <#threads>.\n");        exit(0);    }    nitems = atoi(argv[1]);    nthreads = atoi(argv[2]);    pthread_setconcurrency(nthreads+1);    for(i=0;i<nthreads;++i)    {        count[i] = 0;        pthread_create(&tid_produce[i],NULL,produce,&count[i]);    }    pthread_create(&tid_consume,NULL,consume,NULL);    for(i=0;i<nthreads;i++)    {        pthread_join(tid_produce[i],NULL);        printf("count[%d] = %d\n",i,count[i]);    }    pthread_join(tid_consume,NULL);    exit(0);}void *produce(void *arg){    printf("producer begins work\n");    for(; ;)    {        pthread_mutex_lock(&shared.mutex);        if(shared.nput >= nitems)        {            pthread_mutex_unlock(&shared.mutex);            return ;        }        shared.buff[shared.nput] = shared.nval;        shared.nput++;        shared.nval++;        pthread_mutex_unlock(&shared.mutex);        pthread_mutex_lock(&nready.mutex);        if(nready.nready == 0)            pthread_cond_signal(&nready.cond); //通知消费者        nready.nready++;        pthread_mutex_unlock(&nready.mutex);        *((int*) arg) += 1;    }}void *consume(void *arg){    int     i;    printf("consuemer begins work.\n");    for(i=0;i<nitems;i++)    {        pthread_mutex_lock(&nready.mutex);        while(nready.nready == 0)            pthread_cond_wait(&nready.cond,&nready.mutex); //等待生产者        nready.nready--;        pthread_mutex_unlock(&nready.mutex);        if(shared.buff[i] != i)            printf("buff[%d] = %d\n",i,shared.buff[i]);    }    return;}

结果:

yang@liu:~/syc/第八章$ ./mutexcond  1000000 5producer begins workproducer begins workconsuemer begins work.producer begins workproducer begins workproducer begins workcount[0] = 182165count[1] = 207427count[2] = 183748count[3] = 236607count[4] = 190053

 

0 0
原创粉丝点击