系統程式設計師成長計畫 併發 四 下

2021-04-26 01:16:54 字數 1638 閱讀 9790

讀寫鎖

讀寫鎖在加鎖時,要區分是為了讀而加鎖還是為了寫而加鎖,所以和遞迴鎖不同的是,它無法相容locker介面了。不過為了做到不依賴於特定平台,我 們可以利用locker的介面來抽象鎖的實現。利用現有的鎖來實現讀寫鎖。讀寫鎖的可變的部分已經被locker隔離了,所以讀寫鎖本身不需要做成介面。 它只是乙個普通物件而已:

struct _rwlocker;

typedef struct _rwlocker rwlocker;

rwlocker* rw_locker_create(locker* rw_locker, locker* rd_locker);

ret rw_locker_wrlock(rwlocker* thiz);

ret rw_locker_rdlock(rwlocker* thiz);

ret rw_locker_unlock(rwlocker* thiz);

void rw_locker_destroy(rwlocker* thiz);

o 建立讀寫鎖

rwlocker* rw_locker_create(locker* rw_locker, locker* rd_locker)

return thiz;

}

讀寫鎖的基本要求是:寫的時候不允許任何其它執行緒讀或者寫,讀的時候允許其它執行緒讀,但不允許其它執行緒寫。所以在實現時,寫的時候一定要加鎖,第一 個讀的執行緒要加鎖,後面其它執行緒讀時,只是增加鎖的引用計數。我們需要兩個鎖:乙個鎖用來儲存被保護的物件,乙個鎖用來保護引用計數。

o 加寫鎖

ret rw_locker_wrlock(rwlocker* thiz)

return ret;

}

加寫鎖很簡單,直接加保護受保護物件的鎖,然後修改鎖的狀態為已加寫鎖。後面其它的執行緒想寫,就會這個鎖上等待,如果想讀也要等待(見後面)。

o 加讀鎖

ret rw_locker_rdlock(rwlocker* thiz)

locker_unlock(thiz->rd_locker);

}return ret;

}

先嘗試加保護引用計數的鎖,增加引用計數。如果當前執行緒是第乙個讀,就要去加保護受保護物件的鎖。如果此時已經有執行緒在寫,就等待直到加鎖成功,然後把鎖的狀態設定為已加讀鎖,最後解開保護引用計數的鎖。

o 解鎖

ret rw_locker_unlock(rwlocker* thiz)

else

locker_unlock(thiz->rd_locker);}}

return ret;

}

解鎖時根據狀態來決定,解寫讀直接解保護受保護物件的鎖。解讀鎖時,先要加鎖保護引用計數的鎖,引用計數減一。如果自己是最後乙個讀,才解保護受保護物件的鎖,最後解開保護引用計數的鎖。

從上面讀寫鎖的實現,我們可以看出,讀寫鎖要充分發揮作用,就要基於兩個假設:

o 讀寫的不對稱性,讀的次數遠遠大於寫的次數。像資料庫就是這樣,決大部分時間是在查詢,而修改的情況相對少得多,所以資料庫通常使用讀寫鎖。

o 處於臨界區的時間比較長。從上面的實現來看,讀寫鎖實際上比正常加/解鎖的次數反而要多,如果處於臨界區的時間比較短,比如和修改引用計數差不多,使用讀寫鎖,即使全部是讀,它的效率也會低於正常鎖。

本節示例請到這裡

系統程式設計師成長計畫 併發 四 下

文章出處 作者 李先靜 讀寫鎖讀寫鎖在加鎖時,要區分是為了讀而加鎖還是為了寫而加鎖,所以和遞迴鎖不同的是,它無法相容locker介面了。不過為了做到不依賴於特定平台,我 們可以利用locker的介面來抽象鎖的實現。利用現有的鎖來實現讀寫鎖。讀寫鎖的可變的部分已經被locker隔離了,所以讀寫鎖本身不...

系統程式設計師成長計畫 併發 五

文章出處 作者 李先靜 無鎖 lock free 資料結構 多執行緒併發執行時,雖然有共享資料,如果所有執行緒只是讀取共享資料而不修改它,也是不用加鎖的,比如 段就是共享的 資料 每個執行緒都會讀取,但是不用加鎖。排除所有這些情況,多執行緒之間有共享資料,有的執行緒要修改這些共享資料,有的執行緒要讀...

系統程式設計師成長計畫 併發 五

無鎖 lock free 資料結構 多執行緒併發執行時,雖然有共享資料,如果所有執行緒只是讀取共享資料而不修改它,也是不用加鎖的,比如 段就是共享的 資料 每個執行緒都會讀取,但是不用加鎖。排除所有這些情況,多執行緒之間有共享資料,有的執行緒要修改這些共享資料,有的執行緒要讀取這些共享資料,這才是程...