我们知道多个线程之间有很多内存资源都是共享的,灵活的同时也会造成一些问题,就是当两个线程都要使用同一个资源的时候,例如两个线程同时对一个全局变量进行赋值然后打印,就会出现同步问题,有可能 线程2打印出来的结果实际上是线程1对其赋的值.因此,Linux提供了几种方法用来解决多线程同步问题,在这里主要探究一下互斥锁和条件变量之间的瓜葛.
先简单的说一下互斥锁:
互斥锁是通过锁机制来实现线程间同步的一种方法.在同一时刻它通常只允许一个线程执行一个关键部分的代码.
下面列举操作互斥锁的几个函数:
函数 | 功能 |
---|---|
pthread_mutex_init | 初始化一个互斥锁 |
pthread_mutex_destroy | 注销一个互斥锁 |
pthread_mutex_lock | 加锁,如果不成功,阻塞等待 |
pthread_mutex_unlock | 解锁 |
pthread_mutex_trylock | 测试加锁,如果不成功则立即返回,错误码为EBUSY |
.
而互斥锁使用的常规操作无非是 初始化锁 上锁 操作 解锁 , 最后使用完不要忘记清理锁.
互斥锁一旦被上锁,就只能只能由对其上锁的线程进行解锁,也就是说”解铃还须系铃人”.
再说条件变量:
条件变量是利用线程间共享的全局变量进行同步的一种机制.条件变量宏观上类似于if语句,只要符合一定条件就能执行某段程序.
同样的,列举条件变量相关的几个函数.
函数 | 功能 |
---|---|
pthread_cond_init | 初始化条件变量 |
pthread_cond_wait | 基于条件变量阻塞,无条件等待 |
pthread_cond_timedwait | 阻塞直到指定事件发生,计时等待 |
pthread_cond_singal | 接触指定线程的阻塞,存在多个线程是按入队顺序激活其中一个 |
pthread_cond_broadcast | 解除所有线程的阻塞 |
pthread_cond_destroy | 清除条件变量 |
.
使用条件变量主要包括两个动作,一个等待使用资源的线程等待”条件变量被设置成为真”;另一个线程在使用完资源后,”设置条件为真”,这样就可以保证线程间的同步了.但是这样就处在一个关键问题,就是要保证条件变量能被正确的修改,条件变量就要被保护起来,而实际使用起来,又用到互斥锁来对条件变量进行保护,在这里便产生了疑问,既然条件变量使用过程中又需要使用到互斥锁,干嘛不直接使用互斥锁来保护要进行的操作?
结合从网上查到的资料,自己得出的结论是如果使用互斥锁,当一个进程首先锁定了一个互斥锁然后开始自己的操作,其他后面来的线程想再想锁定这个锁就必须反复查询这个锁的状态,直到发现该锁被第一个线程释放,然后这个时候剩下的线程又开始一拥而上争夺这个锁,这样大量线程频繁的查询锁的状态,然后争夺锁,是一件比较消耗资源的事情,而引入条件变量,我们只是在线程A查询/修改条件变量的那个操作上设置了互斥锁,紧接着,我们就使用pthread_cond_wait()函数使线程A加入指定的条件变量的等待队列,此时该函数会使线程A挂起,在线程A挂起之前该函数还会释放指定的互斥锁,这时候该锁可以由其他线程(例如线程B)锁上,当然线程B在锁定该互斥锁之后也应该使用pthread_cond_wait()函数使其加入条件变量的等待队列,并再次释放该锁,后面当条件变量被pthread_cond_singal()激活后,线程A被唤醒,离开pthread_cond_wait()之前又会重新锁定指定的互斥锁,注意,此时虽然互斥锁被锁上,但是由于线程B此时被条件变量阻塞,处于休眠状态,并不会发生只有互斥锁的那种情况下反复检查互斥锁造成资源消耗的问题,当线程A干完活之后,使用pthread_cond_unlock()函数解开互斥锁,供后续使用.
如此一来,便能够解决只有互斥锁解决同步问题时的弊端.
上面的观点是本人结合《Linux C编程实战》一书和网上的一些资料以及实际测试所得的结论,若是存在知识性错误,欢迎在评论中指出!