临界区:要访问(读写)的共享资源那段代码称之为临界区。这里的代码并不是指C语言等高级语言的代码,而是指机器语言的代码。
互斥:当线程处于临界区并访问共享资源时,其他线程将不会访问相同的共享资源。
锁 :对资源加上一种保护机制,使得外部程序无法进行访问。对应的反向操作称之为解锁。
死锁:两个或以上的进程,进程之间相互等待对方完成特定任务,造成等待的死循环。
饥饿:一个可执行的进程长期无法获取到CPU的使用权。
信号量:有些进程只需要访问共享资源的某个属性,与其他进程访问该共享资源的的内容并不冲突。如果采用加锁的方式,那么会将降低其他任务的执行效率。信号量是比锁 粒度更小的资源保护机制,常用于共享资源集合的互斥访问。
原子操作:指整个操作过程不允许发生中断、上下文切换,而是一口气执行完的一系列操作。
管程:比信号量更高的抽象。它是包含成员变量和函数操作的结构体。它的成员包含1个锁用来指定临界区,0或多个条件变量当作信号量来使用。管程利用一组“信号量”来管理共享资源集合的互斥访问
上下文切换的时机具有不确定性,在1个CPU的情况下,两个线程同时执行相同的代码。如果没有一个合理的同步、互斥机制可能会得到不同的结果。
目录
一、临界区的同步和互斥机制
临界区
1)禁用硬件中断
此方法采用屏蔽硬件中断的方法,让临界区的代码不会被中断,不会被上下文切换,自然也就没有了并发问题。但这只适用于单CPU的系统。而对于多CPU的系统还是无法解决互斥问题的。
2)两个线程间的临界区访问——Dekker算法
此算法需要两个共享的全局变量 turn 和flag 数组,分别表示临界区该被哪一个线程访问 和 表示 哪一个线程想要访问临界区,需给予默认值。其中 i 表示自己的线程标识,j表示另外一个线程的标识。这个算法的原理是:当有两个线程需要访问临界区时,各自发出访问请求 flag[i] = ture ,turn相当于临界区属性,只有进入临界区才可以修改turn标志。而允许进入临界区的条件是当turn的值为自身编号,并且flag [i]为ture时才行。
3)N线程间的临界区访问——Bakery 算法
当由N个进程进入临界区时,每个进程接收到一个数字,得到数字最小的进程进入临界区,如果拿到的数字相同,则进程号pid小的先进入临界区去执行代码。
4)基于锁的原子操作Test and set 方法
利用锁访问的方式。其lock的属性value默认值为0,代表未被锁。有原子方法TestAndSet 方法,这方法执行时不允许被中断。当请求锁方法种,执行testAndSet,如果发现返回值是0,代表可访问,在Test