互斥量:
本质是一把锁,任何对临界资源进行访问的线程都必须先申请锁,如果申请成功,则线程继续执行,进去临界区,实现对共享资源的访问,访问完之后释放锁。其余线程因为申请不到锁而阻塞,直到某个线程释放了这把锁.值得注意的是:只要是对临界资源的访问就需要加上相同的锁,如果有的线程加锁而有的没加锁或则加的锁不是同一把锁,也不会实现对临界量的互斥访问。
unix下的互斥量类型; pthread_mutex_t.
unix 下的互斥量有两种初始化方式:
一种是静态的:name=value,其中的value的值为;PTHREAD_MUTEX_INITILALIZER.
一种是动态的:函数为pthread_mutex_init()这个需要调用pthread_mutex_destory()来销毁锁。
其中pt hread_mutex_init()函数有两个参数,第一个是pthread_mutex_t*,为锁的名字,第二个是const pthread_mutexattr_t,为锁的属性,可以为NULL,这个时候为系统的默认属性,关于属性设置会在后面详细的讨论
pthread_mutex_lock(pthread_mutex_t * mutex);
pthread_mutex_trylock(pthread_mutex_t* mutex);
pthread_mutex_unlock(pthread_mutex_t* mutex);
上面的第一个函数是申请锁的函数,如果锁已经被其他线程占有,则线程阻塞,第三个函数是释放锁的函数。
比较有意思的是第二个函数:trylock,我们知道lock函数在申请锁失败的时候会让线程阻塞起来,那么有么有办法不让线程阻塞呢?那么就是trylock这个函数,这个函数的参数是和lock函数是一样的,但是如果锁已经被其余的线程得到,则这个时候该函数会锁定失败,并且返回EBUSY.如果锁没有被别的线程锁定,则该线程会锁定这把锁。 在使用互斥量的时候,由于使用的不善,可能会出现死锁的问题,那么什么是死锁,又应该怎样避免呢?
死锁:
如果一个进程集合中的每个线程都在等待只能由该组进程中的其他进程才能引发的事件,那么,该组进程是死锁的.
也可以将进程改为一个进程中的线程。
举个简单的例子:A拥有资源1,但是需要B手中的资源2,同时B拥有资源2,同时又需要A手中的资源1.同时资源1和2都是非抢占式的。那么这个时候在A和B之间就存在着死锁,因为A需要的资源2被B占有,则A阻塞,同时又不释放1,B则因为需要的资源1被A占有,而A又阻塞不能释放资源1.这个时候B也只能阻塞。
那么在使用互斥量的时候我们怎样才能避免死锁呢?
方法一:对不同的线程需要同时对相同的互斥量进行加锁的时候,可以固定加锁顺序,这样的话:只有申请到第一把锁的线程才可能顺利的获得其余的锁.
方法二:使用“试加锁与回退”:在使用pthread_mutex_lock()锁住第一把锁的时候,其余的锁使用pthread_mutex_trylock()来锁定,如果返回EBUSY,则释放前面占有的所有的锁,过段时间之后再重新尝试比如:睡一会。
回退算法虽然没有固定的加锁顺序,但是会浪费时间来试锁和回退。这种方法比较灵活确实。
读写锁:
关于读写锁在这里不想做太多的说明,原因是apue上面说的很清楚了,我想写的主要是我自己的一些理解,要是照搬课本,还有什么意思呢?
同时需要注意的一点是:读写锁可以解决读者写者问题的写者优先版本。
条件变量:
首先条件变量是干什么的?条件变量是用来通知共享数据状态信息变化的。可以使用条件变量来通知队列已空,已满或者是非空或者是任何其他的需要由线程处理的共享数据的状态。
为什么需要条件变量:既然条件变量是用来通知某种共享数据状态变化的,那么有时我们可能需要在某种状态之前线程始终处于阻塞状态,很典型的情况就是我们需要对队列进行访问,但是当队列为空的时候我们怎么访问?这个时候就需要条件变量的帮助。
既然条件变量是与一个共享数据相关联,那么一定存在着互斥量.条件变量是与互斥量相关,也与互斥量保护的共享数据相关的信号机制,.在一个条件变量上等待会出现下面的原子性操作:释放相关的互斥量(这里提示我们标准的条件变量的使用方法中一定在条件变量等待函数调用之前锁定互斥量,等待函数为pthread_cond_wait(),从而其他线程可以获得该互斥量,引起条件变量的变化,然后采用广播或则是特定线程的方法通知状态已经改变,则等待条件变量的线程会先锁住锁,再进行下面的操作。
条件变量本身的作用是发信号,而不是互斥,我们需要一个互斥量来同步对共享数据的访问,这就是为什么在等待条件变量的时候需要一个互斥量的原因,同时在解锁与等待条件变量之间原子化,是为了防止解锁之后线程被中断,这个时候其余的线程可能会得到这把锁,并改变条件变量,等到该线程再运行的时候,会等待条件变量,但是这个时候条件变量已经改变了。所以只有原子化才能避免这一点。
所有并发地等待同一个条件变量的线程必须使用相同的互斥量,例如:pthreads 不允许线程1使用条件变量A和互斥量B相关联,而线程2使用条件变量A和互斥量C相关联,但是反过来确实可以的,就是使用同一个互斥量与不同的条件变量相关联。
使用条件变量需要注意的地方:
在锁住相应的互斥量之后和等待条件变量之前,也就是在lock()与condwait之间测试共享数据的状态是很重要的!原因在于线程是异步的,线程之间没有绝对的执行顺序,可能在这个线程执行之前,共享数据的状态已经改变,这个时候因为没有线程在等待该条件变量,则什么也不会发生,但是之后如果在调用condwait之前不进行任何的测试,那就呵呵了,这个线程可能永远的等待下去,因为它所需要的信号可能永远也不会发生。
那么测试共享数据的状态是使用if还是while呢?想想下面的两种情况:
被拦额截的唤醒:因为线程是异步执行的,当某个线程改变了共享数据的状态,并且发出信号之后,这个时候这把和条件变量相关的锁是自有的,任何阻塞在这把锁上的线程都可以获得这把锁,从而不一定是等待条件变量的那个线程,这个时候该线程将继续阻塞,我们称这种情况为被拦截的唤醒,更可能的情况是:在这把锁被其余的线程获得时,可能将共享数据的状态改为假了。这个时候如果不进行重复测试的话,那就呵呵了。
假唤醒:当线程在某个条件变量上等待的时候,等待可能偶然地返回而没有其他的线程广播或则是发信号.这种情况称为假唤醒(我至今没有弄明白怎么回事对于这种情况,难道会是硬件的问题?)那这个时候重复的测试共享数据的状态就显得很是重要了。
下面是模板吧:
pthread_mutex_lock(mutex);
/********some operation*****/
while(某共享变量的状态非我们需要的时候)
pthread_cond_wait(cond1,mutex);
/*********some operation**********/
pthread_mutex_unlock(mutex);
这里提供的仅仅是大致的样子,若是语法错误,见谅。
好了。关于这部分就写这么多了,不早了。明天再写。