同步

讀者寫者問題

想像圖書館桌上的一本參考書。任意多人可以同時讀它而互不打擾——讀不會改變頁面。但若館員想寫上一處更正,其他人都得退開:編輯期間誰都不准讀,而且一次只能有一個編輯者。讀者寫者問題就是:如何允許許多讀者同時讀取共享資料,同時仍讓寫者獲得獨佔、單獨的存取。

標準工具是讀寫鎖(rwlock),一把有兩種模式的鎖。讀者以共享模式取得它:多個讀者可同時共享持有。寫者以獨佔模式取得它:寫者要等到沒有任何讀者、也沒有其他寫者,才能單獨持有。在程式碼中,你讀取前呼叫 rwlock_rdlock()、寫入前呼叫 rwlock_wrlock(),之後解鎖。重點在於奪回普通互斥鎖會白白丟掉的並行:用普通互斥鎖時,連兩個讀者都得串行,當讀遠多於寫時這很浪費。微妙的設計抉擇是策略——若讀者源源而來,「偏好讀者」的鎖可能讓等待的寫者永遠等下去(寫者飢餓);「偏好寫者」的策略則在一旦有寫者等待時就擋住新讀者,以一些讀取吞吐量為代價來修正它。

讀寫鎖在「讀遠多於寫」的資料上大放異彩——設定表、路由快取、記憶體內索引。但它不是互斥鎖的免費升級。讀寫鎖比普通互斥鎖更重(它得追蹤讀者數),所以對於短臨界區間、或讀寫比例約略相當的情形,單純的互斥鎖往往更快。而且它帶來上述的飢餓憂慮。誠實的準則:只有當你量測出「讀為主的爭用」確實是瓶頸時,才動用它。

讀者:rwlock_rdlock(&L); v = config[key]; rwlock_unlock(&L); ——多個可同時跑。寫者:rwlock_wrlock(&L); config[key] = newv; rwlock_unlock(&L); ——單獨跑,當下沒有任何讀者在場。

讀者用共享模式(一次多個),寫者用獨佔模式(一個,單獨)。

讀寫鎖可能餓死寫者(或讀者,視其策略而定)——請去讀你函式庫實作的策略,別假設它公平。它也不會自動比互斥鎖快:那筆額外的讀者計數開銷,在短的或寫得多的臨界區間裡,可能輸給普通互斥鎖。

又稱
reader-writer lockshared/exclusive lockrwlock讀寫鎖共享/獨佔鎖