互斥锁与条件变量区别
mutex体现的是一种竞争,我离开了,通知你进来。
cond体现的是一种协作,我准备好了,通知你开始吧。
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起配合使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
两个线程操作同一临界区时,通过互斥锁保护,若A线程已经加锁,B线程再加锁时候会被阻塞,直到A释放锁,B再获得锁运行,进程B必须不停的主动获得锁、检查条件、释放锁、再获得锁、再检查、再释放,一直到满足运行的条件的时候才可以(而此过程中其他线程一直在等待该线程的结束),这种方式是比较消耗系统的资源的。而条件变量同样是阻塞,还需要通知才能唤醒,线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,该线程就休眠了,应该仍阻塞在这里,等待条件满足后被唤醒,节省了线程不断运行浪费的资源。这个过程一般用while语句实现。当线程B发现被锁定的变量不满足条件时会自动的释放锁并把自身置于等待状态,让出CPU的控制权给其它线程。其它线程 此时就有机会去进行操作,当修改完成后再通知那些由于条件不满足而陷入等待状态的线程。这是一种通知模型的同步方式,大大的节省了CPU的计算资源,减少了线程之间的竞争,而且提高了线程之间的系统工作的效率。这种同步方式就是条件变量。
互斥锁
互斥锁用于保护临界区,使得任何时刻只有一个线程在执行其中的代码。确切的说,互斥锁用于保护多个线程或多个进程分享的共享数据。
posix互斥锁被声明为具有pthread_mutex_t数据类型的变量。
若互斥锁变量是静态分配的,则初始化为:
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
若互斥锁变量是动态分配的(malloc),或者分配在共享内存区中,那么我们必须在运行之时进行初始化:
int pthread_mutex_init(pthread_mutex_t *mptr,const pthread_mutexattr_t *attr);
互斥锁创建与销毁
int pthread_mutex_init(pthread_mutex_t *mptr, const pthread_mutexattr_t *attr);//mptr为互斥锁变量,attr指定其属性 int pthread_mutex_destory(pthread_mutex_t *mptr);
互斥锁上锁与解锁
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mptr);//尝试上锁时,该锁已经被锁,则阻塞等待。 int pthread_mutex_trylock(pthread_mutex_t *mptr);//尝试上锁时,该锁已经被锁,则返回BUSY错误。 int pthread_mutex_unlock(pthread_mutex_t *mptr);
条件变量
互斥锁用于上锁(竞争),条件变量用于等待(同步)。条件变量的类型是pthread_cond_t的变量。
条件变量需要互斥锁配合的原因:每个条件变量总是有一个互斥锁与之关联,调用wait函数前必须用lock加锁,防止多个线程同时请求wait。(保护wait)
条件变量的休眠与唤醒
int pthread_cond_init(pthread_cond_t *cptr, const pthread_condattr_t *attr);//cptr为条件变量,attr指定其属性 int pthread_cond_destory(pthread_cond_t *cptr);
条件变量的休眠与唤醒
#include <pthread.h> int pthread_cond_wait(pthread_cond_t *cptr,pthread_cond_t *mptr);//cptr为条件变量地址;mptr为互斥量地址。 int pthread_cond_signal(pthread_cond_t *cptr);//signal 函数用来唤醒 wait 函数。
条件变量静态初始化:
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
生产者消费者使用锁与条件变量例子
int nitems; int buff[1000]; struct{ pthread_mutex_t mutex; int nput; int val; } put = { PTHREAD_MUTEX_INITIALIZER }; struct{ pthread_mutex_t mutex; pthread_cond_t conf; int nready; }nready = { PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER };//对锁与条件变量进行初始化 void *produce(void *arg) { for(;;) { pthread_mutex_lock(&put.mutex); if(put.nput >= nitems) { pthread_mutex_unlock(&put.mutex); return NULL; } buff[put.nput] = put.nval; put.nput++; put.nval++; pthread_mutex_unlock(&put.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; 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(buff[i] != i) printf("buff[%d] = %d\n",i,buff[i]); } return NULL; }
条件变量:定时等待与广播
广播:
pthread_cond_signal只唤醒等待在相应条件变量上的单个线程,若一个线程认定有多个其他线程被唤醒,则可以调用pthread_cond_broadcast唤醒阻塞在相应条件变量上的所有线程。
int pthread_cond_broadcast(pthread_cond_t *cptr);
定时等待:
int pthread_cond_timewait(pthread_cond_t *cptr,pthread_mutex_t *mptr,const struct timespec *abstime);
struct timespec { time_t tv_sec;//seconds long tv_nsec://nanoseconds }
pthread_cond_timewait允许线程就阻塞时间设置一个限制值,该限制值指定这个函数必须返回时的系统时间(绝对时间),即便相应的条件变量还没有收到信号。若发生超时情况,函数返回ETIMEDOUT错误。