一. 概念:
1. 同步和鎖:
1. 二元信號量:最簡單的一種鎖;只有佔用或者沒有佔用;他適合唯一一個線程獨佔的資源,一旦被佔用,其他所有線程將會等待直到鎖釋放;
2. 多元信號量:簡稱信號量,允許多個線程併發訪問的資源
3. 互斥量:與二元信號量很相似,資源僅同時允許一個線程訪問,但是有所不同,**信號量可以在一個線程獲取並且阿紫另一個線程釋放,而互斥量必須試獲取的線程才能釋放**
4.臨界區: 比互斥量更嚴格的同步手段。**互斥量和信號量在系統的任何進程都可以看見,臨界區的範圍只在本進程可見,所以如果只爲了在進程內部是用的話使用臨界區會帶來速度上的優勢並能夠減少資源佔用量** 。因爲互斥量是跨進程的互斥量一旦被創建,就可以通過名字打開它。
5.讀寫鎖:對同一個鎖,讀寫鎖有兩種獲取方式,共享和獨佔
讀寫鎖狀態 |
共享方式獲取 |
獨佔方式獲取 |
自由 |
成功 |
成功 |
共享 |
成功 |
等待 |
獨佔 |
等待 |
等待 |
6.條件變量:對於條件變量,線程可以有兩種操作,可以等待條件變量,一個條件可以被多個線程等待;線程也可以喚醒條件變量,此時某個或所有等待此條件變量的線程將被喚醒並持續支持。**條件變量可以讓多線程等待同一個事件發生,當事件發生,所有的線程又可以一起恢復**
2.可重入與線程安全:
1.一個函數可被重入:
1).多個線程同時執行該函數不會出現不良後果;
2).函數可以自己調用自己;
3.過度優化:
原因:由於編譯器優化,提高訪問速度,可能會將某些變量放入線程寄存器裏,然而線程寄存器是相互獨立;又或者編譯的順序優化毫無相干的兩條指定導致執行順序變化;
x可能依舊爲1
```c++
x= 0
Thread1 Thread2
lock() lock()
x++ x++
unlock() unlock()
```
···
x=y=0
Thread1 Thread2
x=1 y=1
r1 =y r2=x
···
r2 = r1= 0 是可能的 當實行順序發生瞭如下的改變
```
x=y=0
Thread1 Thread2
r1 = y y=1
x=1 r2=x
```
1.volatile關鍵字阻止過度優化
1).阻止編譯器將變量緩存在寄存器內而不寫回內存
2).阻止編譯器調整操作**volatile變量**的指令順序
2.關於單例模式的指令換順序
```
volatile T* p = 0;
T * GetInstance(){
if (p == NULL)
{
lock();
if( p ==NULL){
p = new T;
}
unlock();
}
}
return p;
}
```
這樣的代碼看似沒有問題,但是new有兩個步驟組成而賦值就有三個步驟:
(1).分內存(malloc)
(2).在內存位置調用構造函數
(3).將內存地址賦值給p
在這3個步驟中,後兩個步驟是可以順序顛倒的,也就是說已經分配了內存,但是卻沒有構造完成,如果這時有另一個線程調用,那麼p將不爲null,將返回未構造完成的p的指針回去,就會導致程序的崩潰;
解決方法: **調用barrier指令,防止在barrier的指令交換到barrier之後執行;**
```
volatile T* p = 0;
T * GetInstance(){
if (p == NULL)
{
lock();
if( p ==NULL){
T* tmp = new T;
barrier();
p = tmp;
}
unlock();
}
}
return p;
}
```