线程间的同步和通信
了解过了进程间的同步和通信,下面了解线程间的同步和通信。
相关知识点:进程和线程;信号量机制;进程同步互斥;进程间通信。
多线程OS通常提供多种同步机制。
互斥锁(mutex)
同进程互斥类似,它实现线程间对资源的互斥访问。
由于操作互斥锁的时间和空间开销都较低,因而较适合于高频度使用的关键共享数据和程序段。互斥锁有两种状态,即开锁(unlock)和关锁(lock)状态。
当一个线程需要读/写共享数据段时,首先判别该共享数据段所设置的mutex 的状态,如果它已处于关锁状态(不可用),则线程将被阻塞;如果 mutex 是处于开锁状态(可用),则将 mutex 关锁(设置标识)后便去读/写该数据段。在线程完成读/写后,必须再发出开锁命令将 mutex 开锁(恢复标识),同时还须将阻塞在该互斥锁上的一个线程唤醒,其它的线程仍被阻塞在等待 mutex 打开的队列上。
线程对资源的互斥访问 与 之前总结的临界资源、临界区一样。
上述读/写共享数据段的过程相当于记录型信号量,互斥锁是资源,资源数为1。关锁即资源数为0了。下图是记录型信号量的wait()和signal()描述,相当于互斥锁的关锁、开锁。
记录型信号量可能出现死锁,一个进程要求临界资源越多、死锁可能性越大。信号量机制中有详细总结。
线程的互斥锁,也同样存在可能死锁的问题。
类似的:
有两个线程A、B。两个临界资源D、E,互斥锁分别是Dmutex、Emutex。
若A、B都需要D和E,按下面情况,无外界干预线程A、B即陷入死锁状态。同样需要资源越多,线程死锁可能也越大。
thread A: 对Dmutex关锁成功,再对Emutex关锁失败(需要E,但E被线程B持有),于是A阻塞,而Dmutex一直处于关锁状态
thread B: 对Emutex关锁成功,再对Dmutex关锁失败(需要D,但D被线程A持有),于是B阻塞,而Emutex一直处于关锁状态
条件变量
为了解决类似上述互斥锁中死锁问题引入了条件变量。
每一个条件变量通常都与一个互斥锁一起使用,亦即,在创建一个互斥锁时便联系着一个条件变量。
单纯的互斥锁用于短期锁定,主要是用来保证对临界区的互斥进入。而条件变量则用于线程的长期等待,直至所等待的资源成为可用的资源。
利用互斥锁和条件变量访问资源,大致描述如下:
//定义 mutex:互斥锁 resource:资源 condition:条件变量 ///申请过程 Lock mutex if(resource busy) wait(condition); mark resource as busy; unlock mutex; ///释放过程 Lock mutex mark resource as free; unlock mutex; wakeup(condition);
线程先对mudex关锁,进入临界区。若资源忙不可用,线程变转入等待队列(条件变量相关的队列),然后对mudex开锁;若资源空闲,标记资源为忙碌,再对mudex开锁。
使用资源的线程完成释放资源时,mudex关锁进入,标记资源空闲,再对mudex开锁,然后从条件变量的队列中唤醒一个线程。唤醒的就是之前进入等待中的线程,能够继续执行了。
类似管程机制中的条件变量。
信号量机制
进程同步工具:信号量机制。也可用于多线程OS中,实现多线程或进程之间的同步。
1) 私用信号量(private samephore)
当某线程需利用信号量来实现同一进程中各线程之间的同步时,可调用创建信号量的命令来创建一私用信号量。
其数据结构存放在应用程序的地址空间中。私用信号量属于进程所有,OS并不知道私用信号量的存在。
2) 公用信号量(public semaphort)
公用信号量是为实现不同进程间各线程之间的同步而设置的。
由于它有着一个公开的名字供所有的进程使用,故而把它称为公用信号量。其数据结构是存放在受 保护的系统存储区中,由 OS 为它分配空间并进行管理,故也称为系统信号量。