目錄
1. 同步
協調多線程對共享數據的訪問,任何時刻只能有一個線程執行臨界區代碼。
2. 臨界區
定義:對共享內存進行訪問的程序片段。
實現方法:
- 禁用中斷:沒有中斷,沒有上下文切換,因此沒有併發。
- 軟件方法:如 Peterson 算法。複雜、需要兩個進程間的共享數據項;需要忙等待,浪費 CPU 時間
- 更高級的抽象方法:硬件提供了一些同步原語,操作系統提供更高級的編程抽象來簡化進程同步。如鎖。
3. 信號量(semaphore)
信號量是操作系統提供的一種協調共享資源訪問的方法,用信號量表示系統資源的數量。
一種抽象的數據類型,由一個整型變量(sem)和兩個原子操作組成,操作系統保證 PV 操作的原子性。
- P()(嘗試減少):sem 減 1,如 sem < 0,進入等待,否則繼續
- V()(增加):sem 加 1,如 sem <= 0,喚醒一個等待線程
3.1 信號量實現互斥訪問
3.2 信號量實現同步等待
3.3 信號量實現生產者消費者問題
問題抽象:
- 任何時刻只能有一個線程操作緩衝區(互斥訪問)
- 緩衝區空時,消費者必須等待生產者(條件同步)
- 緩衝區滿時,生產者必須等待消費者(條件同步)
用信號量描述每個約束:
- 二進制信號量 mutex
- 資源信號量 fullBuffers
- 資源信號量 emptyBuffers
代碼:
4. 管程
一種用於多線程互斥訪問共享資源的程序結構。任意時刻最多隻有一個線程執行管程代碼,正在管程中的線程可臨時放棄管程的互斥訪問,等待事件出現時恢復。
組成:
- 一個鎖:控制管程代碼的互斥訪問;
- 0 個或多個條件變量:管理共享數據的併發訪問。條件變量是管程內的等待機制,每個條件變量表示一種等待原因,對應一個等待隊列。Wait() 操作:將自己阻塞在等待隊列中,喚醒一個等待者或釋放管程的互斥訪問。Signal() 操作:將等待隊列中的一個線程喚醒。
管程解決生產者消費者問題:
5. 經典同步問題之哲學家就餐問題
五個哲學家圍着一張圓桌,每個哲學家面前放着食物。哲學家的生活有兩種交替活動:吃飯以及思考。當一個哲學家吃飯時,需要先拿起自己左右兩邊的兩根筷子,並且一次只能拿起一根筷子。思考時將兩支叉子放回原處。如何保證哲學家們的動作有序進行?不會出現有人永遠拿不到叉子。
使用信號量解決:
根據哲學家不同編號,使其拿起兩隻筷子的順序不同,避免五個哲學家都拿到一部分資源構成環路的情況。
6. 讀者-寫者問題
對共享數據的讀寫,同一時刻,允許有多個讀者同時讀;沒有讀者時寫者才能寫,沒有寫着時讀者才能讀;沒有其他寫者時才能寫。
使用信號量解決:
讀者優先策略:只要有讀者正在讀狀態,後來的讀者都能直接進入,如讀者持續不斷進入,則寫者就處於飢餓狀態。
寫者優先策略:只要有寫者就緒,寫者應儘快執行寫操作;如寫者持續不斷進入,則讀者就處於飢餓狀態。