一. 進程互斥的實現方式
1. 軟件方式:
保護臨界區, 自己編寫代碼來實現對進程的控制.
Dekker算法
Peterson算法
Lamport算法等
2. 硬件方式:
使用特殊指令保護臨界區.
開關中斷指令
測試並加鎖指令
交換指令
忙等待, 自旋鎖
二. Lamport面包店算法
解決多線程並發訪問同一個共享資源的互斥問題
這個思想來自於面包店, 醫院等, 需要排隊取號的場所. 顧客進入面包店前,首先抓取一個號碼,然后按號碼從小到大的次序依次進入面包店購買面包.
前提:
面包店按由小到大的次序發放號碼
兩個或兩個以上的顧客有可能得到相同號碼
當多個顧客抓到相同號碼,則按顧客名字的字典次序排序
基本思想:
發號器按由小到大的次序發放號碼. 進程進入臨界區前先抓取一個號碼, 然后按號碼從小到大的次序依次進入臨界區. 若多個進程抓到相同的號碼則按進程編號依次進入.
偽算法:
1 // 變量說明: 2 // i 表示當前進程PID 3 // j 表示當前迭代到的進程PID 4 // choosing[i] 表示當前進程i是否正在取號, 默認值為false 5 // number[i] 表示當前進程i的排隊號, 默認值為0 6 7 process(i) { 8 while (true) { 9 // 當前進程i正在取號 10 choosing[i] = true; 11 // number為上一個已發放的排隊號加1 12 number[i] = 1 + max(number[1], number[2], ..., number[n-1]); 13 // 當前進程i取號完畢 14 choosing[i] = false; 15 16 // 迭代所有進程 17 for (j = 0; j < n; j++) 18 { 19 // 若當前迭代到的進程j正在取號, 則等待其取號完畢 20 while(choosing[j]); 21 22 // 同時滿足, 當前進程才能通過 23 while (number[j] != 0 && (number[j], j) < (number[i], i)); 24 } 25 26 // 臨界區代碼 27 28 // 當前進程注銷排隊號 29 // 一旦線程在臨界區執行完畢,需要把自己的排隊簽到號碼置為0,表示處於非臨界區 30 number[i] = 0; 31 32 // 其它代碼 33 34 } 35 }
注意:
1) 進程需要排隊等待的三種情況:
情況1: 存在沒有取得排隊號的進程
情況2: 當前迭代到的進程沒有取得排隊號
情況3: 當前迭代到的進程的排隊號小於當前進程的排隊號, 或當前迭代到的進程PID小於當前進程PID
2) 只有當前進程注銷了排隊號, 在排隊的其它進程才能進入臨界區, 滿足進程互斥和有限等待
3) 符號說明: (a, b) < (c, d) 表示 (a < c) or ((a == c) and (b < d))
4) 使用choosing數組是必須的, 假設不使用choosing數組, 就可能會出現這種情況: 設進程i的優先級高於進程j(即 i < j), 兩個進程獲得了相同的number,
進程i在寫number[i]之前, 被優先級低的進程j搶先獲得了CPU時間片, 這時進程j讀取到的number[i]為0, 因此進程j進入了臨界區. 隨后進程i又獲得CPU時間片, 它讀取到的number[i]與number[j]相等, 且i < j, 因此進程i也進入了臨界區. 這樣, 兩個進程同時在臨界區內訪問, 可能會導致數據腐爛(data corruption). 算法使用了choosing數組變量, 使得修改number數組的元素值變得"原子化", 解決了上述問題
后續:
在Linux上實現編寫C程序實現面包店算法
參考:
1. 《算法之美》—進程互斥軟件算法(Lamport面包店算法和Eisenberg算法)
https://www.xuebuyuan.com/647028.html
2. 面包店算法 - CSDN
https://blog.csdn.net/yucan1001/article/details/7973075