学了两个周,也有了线程、进程的一些浅层理解,其中感觉比较难理解的就是线程的同步,线程的同步用了三种方法,一种是互斥锁的运用,还有就是条件变量的使用,最后是异步信号。
- 互斥锁基础知识
- 条件变量基础知识
- 条件变量与互斥锁的运用
- *pthread_cond_wait理解
- 异步信号
- 信号量
互斥锁
互斥锁函数:
pthread_mutex_init 加锁前初始化
pthread_mutex_destroy 解锁完成后释放互斥锁占用资源
pthread_mutex_lock 加锁
pthread_mutex_unlock 解锁
pthread_mutex_tylock 测试加锁
互斥锁就是通过锁的机制,让在同一时刻只允许一个线程执行一个关键的代码,确保一次性执行完毕,不被中断。
条件变量
条件变量函数:
pthread_cond_init 初始化
pthread_cond_wait 基于条件变量阻塞,无条件等等
pthread_cond_timewait 阻塞直到指定事件发生,一段时间等待
pthread_cond_signal 发出激活信号
pthread_cond_broadcast 解除线程所有的阻塞
pthread_cond_destroy 清楚条件变量
条件变量两个操作:首先一个等待使用资源的线程等待条件变量被设置为真,第二个操作就是另一个线程使用完那块公共资源后,把条件设置为真。
条件变量与互斥锁的关系
要保证变量正确被修改,条件变量也需要受到保护,此时用了互斥锁来保护。如果不使用两者搭配的话,当一个线程只使用互斥锁的话,当其他线程到这个程序的时候就会来判断是否上锁,若上锁了就等待解锁,这个时间里可能会有很多线程来判断,然后阻塞在这里,当调用锁的线程释放锁后,那么被阻塞的线程又会来抢夺这个资源,从而造成了资源时间空间的浪费。而当加上了条件变量的时候,被阻塞的线程就会变得有序,在一个队列里排队,当接受到激活信号之前不会反复检查是否该锁已经释放,只需要一个通知(激活信号)来告诉这个线程,你所需要的那个锁已经释放了,现在你可以用了,此时整个过程就变的有序,而不是因为抢夺来消耗资源了。二者的关系操作可以形象看作从一个无序的占用资源变成一个有序的节约资源的一个改变,所以条件变量和互斥锁二者使用能够保证条件变量的正确修改。
pthread_cond_wait理解
在条件变量里面,锁的函数很重要,其中起到关键作用之一的函数便是这个等待函数吧。
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
—pthread_cond_wait百度百科
函数原型:
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)
该函数的两个动作:首先释放由第二个参数指向的锁,解锁后,把第一个参数cond的条件变量指向条件阻塞变量,直到条件信号被激活。
当一个线程在使用条件变量和互斥锁的时候,在条件不满足的时候(即条件信号为阻塞的时候),通过这个函数,该线程的锁会不断被解锁最后又被加锁,直到条件满足,被激活后此时会到重新上锁那一步,最后返回该函数,此时该函数执行语句结束,才会往下面的程序进行。
注意两点:
1) 在thread_cond_wait()之前,必须先lock相关联的mutex, 因为假如目标条件未满足,pthread_cond_wait()实际上会unlock该mutex, 然后block,在目标条件满足后再重新lock该mutex, 然后返回.–这点非常重要
2) 为什么是while(sum<100),而不是if(sum<100) ?这是因为在pthread_cond_signal()和pthread_cond_wait()返回之间,有时间差,假设在这个时间差内,还有另外一个线程t4又把sum减少到100以下了,那么t3在pthread_cond_wait()返回之后,显然应该再检查一遍sum的大小.这就是用while的用意
pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。
pthread_cond_wait() 必须与pthread_mutex_lock() 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。
pthread_cond_signal()必须和pthread_mutex_unlock(lock_s) 配套使用,在发送信号或者广播之前,一定要先unlock mutex,因为阻塞在条件变量上的线程被唤醒的时候,要对mutex加锁,如果该线程被唤醒,而此时通知线程仍然锁住互斥锁,泽被唤醒线程立刻阻塞在互斥锁上。
异步信号
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。
“同”字从字面上容易理解为一起动作
其实不是,“同”字应是指协同、协助、互相配合。
如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。例如Window API函数SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者。
在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
看到这里,很懵。异步信号怎么实现同步的,回到最初的起点,线程同步是什么,其实线程同步不是同时执行一段代码,而是协调协商来执行的,就是一个有序的过程,不让他们争抢资源,争抢一般都会有意外,很暴力……所以要有序进行,协商有次序进行。就和排队一个道理。
首先信号与任何线程都是异步的,它到达每一个线程的时间都不一样,那么多个线程接受异步信号的时候,只有一个线程首先获得该信号。此时如果并发的多个同样的信号被送到一个进程,该进程有许多线程,这些并发的同样的信号会被不同的线程获取,而由于是并发的,只是在一个时间段里,在这个时间段里还是有先后顺序的,此时所有线程屏蔽了这些信号,线程收到信号并被挂起然后激活处理的过程还是一个排队执行的并发过程,只是看似是同时发生的。
信号量
引用微博上看见的一个片段
“这段共享内存的使用是有竞争条件存在的,从文件锁的例子我们知道,进程间的通信绝不仅仅是通信那么简单,还需要在处理类似这样的临界区代码。在这里,我们也可以使用文件锁来处理,但是共享内存使用文件锁未免显得太不协调了。除了不方便以及效率低下以外,文件锁还不能够进行更高的进程控制。所以,我们在此需要更高级的进程同步控制原语来实现相关功能,这就是信号量的作用”