不管是互斥鎖、Semaphore、Mutex、WaitHandle 的那一種作法,其主要的目的,都是為了確保多執行緒爭相存取共用資源時,共用資源內容的正確性。
在設計並發性的系統中,許多前輩與神人,整理出三個原則。分為原子性、有序性、可見性。
在以前,原子被認為為是最小的粒子,無法在再分割。雖然現在推翻這個概念,但還是習慣使用這個名詞。
以筆者的解讀,原子性指的就是**一個流程或操作之中,無法被外部因素影響,而造成中斷。**對具有原子性的操作而言,只有兩種可能。
像 Lock
機制,Lock 內的區塊,就視為一個不可分割的執行個體。在執行期間,不會被任何外部因素打斷或影響。
lock(obj)
{
// 此區塊可視為一個不可分割的整體
}
原子性的區塊大,可以確保更多的操作,但相對的,可能減低系統的效能。區塊小,可能無法確保操作。還是要依據需求,進行評估原子性區塊的範圍。
使用 Queue 就是因為有著 FIFO 這種有順序的操作方式。相同的,多個執行緒對共用資源操作,可以依據對共用資源存取的請求順序,依序操作。
筆者的理解,有序性,就是會依先後進入的順序,進行操作或存取。Lock
、Mutex
就是屬於這種的。
這時,再把前面的圖回顧一下。
Producer 1、Producer 2、Customer 1 三者前後請求對 Queue 的操作,而實際的操作順序,也是與請求順序相同。這就是有序性。
今天,我去超商買走最後一瓶飲料,其他顧客要買時,就會發現飲料沒了。相同的情境,套到系統內,當共用資源被某一個執行緒修改,其他存取共用資源的執行緒也會發現資料的變更,這就是可見性。
在運用原子性的操作後,所有取存共用資源的執行緒,所得到的,都是變更後的最新的資訊,
如果系統為效能的考量,採用了快取的機制。而快取資料,一般是固定間隔才會更新資料,所以存取快取而來的資料,不一定是最新的。
這種情況,就是為了效能而犧牲了部份的可見性。