为了允许在线程或进程之间共享数据,同步是必须的,互斥锁和条件变量是同步的基本组成部分.
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] = 206634 count[1] = 209573 count[2] = 188241 count[3] = 197221 count[4] = 198331 消费者正在工作!
可以看出,生产者进程生产完,消费者进程才开始工作! 2.通过条件变量判断判断,当消费者等待生产者,当生产者有生产时;即唤醒消费者,消费者开始工作.代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #define MAXNITEMS 1000000 #define MAXNTHREADS 100 int 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 5 producer begins work producer begins work consuemer begins work. producer begins work producer begins work producer begins work count[0] = 182165 count[1] = 207427 count[2] = 183748 count[3] = 236607 count[4] = 190053