多執行緒互斥鎖訪問演算法(上)------Peterson演算法

風箏丶發表於2017-11-08

你好!這裡是風箏的部落格,

歡迎和我一起交流。

在多執行緒存在的環境中,除了堆疊中的臨時資料之外,所有的資料都是共享的。如果我們需要執行緒之間正確地執行,那麼務必需要保證公共資料的執行和計算是正確的。簡單一點說,就是保證資料在執行的時候必須是互斥的。否則,如果兩個或者多個執行緒在同一時刻對資料進行了操作,那麼後果是不可想象的。

那麼,該怎麼去實現一個資源(資料)的互斥訪問呢?
那就是這章所講的Peterson演算法,它可以控制兩個執行緒訪問一個共享的單使用者資源而不發生訪問衝突。Gary L. Peterson於1981年提出此演算法 。
我們看下程式碼實現:

#define true    1
#define false   0
int lock;
int hold [2]={false};

void enter_lock(int thread_id)
{
    hold[thread_id]=true;/*標記佔有資源*/
    lock=thread_id;
    while(lock==thread_id&&(hold[1-thread_id]==true));/*阻塞,等待排程*/
}

void exit_lock(int thread_id)
{
    hold[thread_id]=false;/*標記釋放資源*/
}

這就是Peterson演算法的核心程式碼,當我們需要訪問臨界區(互斥資源)時:

void process_0(void)//執行緒0
{
    enter_lock(0);
    //臨界區
    //訪問資源
    exit_lock(0);
}
void process_1(void)//執行緒1
{
    enter_lock(1);
    //臨界區
    //訪問資源
    exit_lock(1);
}

我們可以來模擬一下:
當有兩個執行緒(執行緒A,thread_id=0、執行緒B,thread_id=1)來搶佔資源時,

我們可以假設三種情況來模擬:

一:當執行緒B不佔有資源但是執行緒A想佔有資源時,
執行緒A的enter_lock函式while裡&&右邊條件不成立,所以while不成立,執行緒A成功佔有資源。

二:當執行緒B正在佔有資源,但是系統發生排程使得執行緒A也想佔有資源時,
執行緒A的enter_lock函式裡while成立,使得執行緒A函式阻塞,
待執行緒B呼叫exit_lock函式後才會使得執行緒A裡的while得以向下執行。

三:當執行緒B執行:”lock=thread_id;”語句之後,系統發生呼叫使得執行緒A也想佔有資源時,
執行緒A的enter_lock函式裡while成立,使得執行緒A函式阻塞,當系統排程使得執行緒B繼續執行時,
因為全域性變數lock被執行緒A更改了,所以執行緒B中while不成立,執行緒B成功佔有資源,待執行緒B釋放資源後,
執行緒A中while右半部不成立,所以也使得執行緒A佔有資源。

顯然易見,Peterson演算法有個侷限性,他只支援兩個執行緒,如果在多一個執行緒,則會出現錯誤。
為了解決這個問題,下一章會講另一種演算法:多執行緒互斥鎖訪問演算法(下)——Lamport演算法(麵包店演算法)

Peterson演算法不需要原子(atomic)操作,它是純軟體途徑解決了互斥鎖的實現。但需要注意限制CPU對記憶體的訪問順序的優化改變(編譯器的一個優化點)。因為它依賴於這個時序實現:
先寫入hold[thread_id]=true;再寫入lock=thread_id;,否則會出現死鎖。

相關文章