經典同步問題與並行程式設計

讀者-寫者問題(readers-writers problem)

想像圖書館桌上有一本人人可讀的公用參考書。許多人可以同時圍著它一起閱讀,毫無問題——閱讀並不改變任何東西。但假設館員需要黏上一張勘誤。在這次編輯進行時,誰都不該在讀,因為可能看到一半舊、一半新的文字;同時也不該有第二位編輯在寫。讀者-寫者問題正是抓住共享資料的這種不對稱:任意數量的讀者可以同時存取,但寫者需要獨佔存取——獨自一人,把所有讀者與其他寫者都擋在外面。

這比單純的互斥鎖寬鬆,後者會強迫讀者輪流,即使並行讀取完全安全。標準解法用一個由自己的小鎖保護的讀者計數,外加一把寫者鎖。第一位到的讀者取得寫者鎖(把寫者擋在外),之後的讀者只是把計數加一便進場,最後離開的讀者釋放寫者鎖。寫者則直接把寫者鎖佔為己有。基於這個概念的真實鎖元件就是讀寫鎖(在 pthreads 中是 pthread_rwlock_t):呼叫者要求的是共享的讀鎖或獨佔的寫鎖。

難處——也是它之所以是一道教學問題、而不只是一份食譜的原因——在於政策。上面的簡單解法偏向讀者:只要讀者源源不絕地進來,寫者鎖永遠不會空出,等待的寫者可能被永遠餓死。把規則反過來改成偏向寫者,能修好這個,但此時穩定湧入的寫者又會餓死讀者。真實系統通常想要一個公平版本,限定任一方等待的時間上限。所以讀者-寫者問題與其說是某個答案,不如說是有意識地選擇一個公平政策,並承受它的取捨。

讀者進入:lock(countMutex); if (++readers == 1) wait(writeLock); unlock(countMutex)。讀者離開:lock(countMutex); if (--readers == 0) signal(writeLock); unlock(countMutex)。寫者:wait(writeLock) ... signal(writeLock)。

經典的偏向讀者解法——對讀取繁重的資料很快,但只要讀者持續到來,寫者就可能挨餓。

讀寫鎖唯有在讀遠多於寫、且每段臨界區間夠長時才划算。對於短或讀寫均衡的工作負載,它額外的簿記往往讓它比單純的互斥鎖還慢。

又称
讀寫問題shared-exclusive locking