廢話開篇
iOS 下的多線程的技術的應用衍生出了鎖的機制,試想,如果 iOS 下沒有多線程的概念,所有的代碼都會在同步環境下執行,那么,也就不會產生爭奪資源情況的發生,當然,也就沒有辦法利用多核的優勢。所以,多線程的應用是廣布的,而鎖的應用是局部的,所以,二者應相輔相成,來達到提高運行效率的同時提高程序運行的穩定性。
思考一、對于鎖的類型的理解
基本的三種鎖的類型:互斥鎖、自旋鎖、讀寫鎖。
其中,互斥鎖 多線程在訪問加鎖中的臨界區前,會進入休眠,一直等待解鎖后系統調度
;自旋鎖 多線程在訪問加鎖中的臨界區前,不進入休眠,會一直忙等。 讀寫鎖 是一種思想,本質就是利用 互斥鎖 來實現特定的應用場景:多讀并行、讀與寫互斥,寫與寫互斥;對于其他的類型的鎖,比如:信號量、條件鎖、遞歸鎖,可以理解為是由以上基本類型的鎖實現的上層封裝。
思考二、讀寫鎖的實現邏輯
如果有這樣一塊公共資源,它的寫入是比較耗時,那么,在這段時間內要避免程序再次的進行寫入操作和讀取操作,這樣可以避免產生爭奪資源的問題,當然,讀取的過程可以并行。
先上一段代碼,這里模擬一個比較耗時的寫入過程,在模擬一個快速讀取的過程。
//讀寫鎖 - (void)readAndWriteLock { //寫 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"我開始寫"); for (int i = 0; i < 10000; i++) { } NSLog(@"我寫完了"); }); //讀 for (int i = 0; i < 10; i++) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"我開始讀%d",i); for (int j = 0; j < i; j++) { } NSLog(@"我讀完了%d",i); }); } }
在寫入的異步內執行了循環 10000 次的操作來模擬耗時任務;后面開辟了多線程進行讀取,在讀取的異步里,實現簡單的不同數量的循環來模擬耗時任務,這里的耗時次數遠小于寫入。
打印如下
通過輸出可以清晰的看到,寫入過程中還有很多讀取的操作在同時進行。致于是先開始讀取還是開始寫入,這里其實并不用去關心,本身它們也是由系統決定的,但是,這里需要控制一下代碼,來實現讀寫互斥,即在寫的過程中,禁止讀取操作的介入。同時,也需要實現寫寫互斥。對于,多讀,其實這里并不需要過多干涉,因為,本身就允許多讀的邏輯。
思考三、簡單封裝讀寫鎖,滿足讀寫邏輯
利用互斥鎖封裝讀取加、解鎖;寫入加、解鎖
//初始化讀取鎖 static pthread_mutex_t r_plock = PTHREAD_MUTEX_INITIALIZER; //初始化寫入鎖 static pthread_mutex_t w_plock = PTHREAD_MUTEX_INITIALIZER; //記錄當前讀取次數,因為只要其值不為0,那么,就說明程序在讀取操作,這里停止寫入操作 static int current_read_times = 0;
進行讀取加鎖
//讀加鎖 - (void)readLock { pthread_mutex_lock(&r_plock); current_read_times ++; if (current_read_times == 1) { pthread_mutex_lock(&w_plock); } pthread_mutex_unlock(&r_plock); }
這里首先進行 讀取鎖 加鎖,這里加鎖的目的并不是鎖定讀取過程,而是鎖定了修改 current_read_times 的過程,當 current_read_times 變更后,如果為 1,那么,就對 寫入鎖 加鎖,這個寫入鎖就是鎖住寫入過程的。這兩個鎖的應用部分是有區別的。
進行讀取解鎖
//讀解鎖 - (void)readUnLock { pthread_mutex_lock(&r_plock); current_read_times --; if (current_read_times == 0) { pthread_mutex_unlock(&w_plock); } pthread_mutex_unlock(&r_plock); }
這里首先進行 讀取鎖 加鎖,目的還是對 current_read_times 修改的鎖,可以總結一下,讀寫鎖本質并不對多讀進行限制,所以,這里的讀取鎖是鎖住 current_read_times 修改過程,在加鎖的情況下進行 寫入鎖 狀態的變更,實現 讀與寫的互斥。后面進行狀態判斷,如果 current_read_times 為 0,說明當前所以讀取完成了,那么,對 寫入鎖 進行解鎖,解鎖后寫入操作就可以正常進行。這里解鎖的判斷為 == 0 與 加鎖的判斷 == 1 是一對,這樣就滿足了,一個加鎖過程對應一個解鎖條件。不會出現只有加鎖后而沒有解鎖的情況。
進行寫入加鎖
//寫加鎖 - (void)writeLock { pthread_mutex_lock(&w_plock); }
這里僅僅是對 寫入 操作進行加鎖。
進行寫入解鎖
//寫解鎖 - (void)writeUnLock { pthread_mutex_unlock(&w_plock); }
這里僅僅是對 寫入 操作進行解鎖。
最后的代碼
//讀寫鎖 - (void)readAndWriteLock { //寫 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self writeLock]; NSLog(@"我開始寫"); for (int i = 0; i < 10000; i++) { } NSLog(@"我寫完了"); [self writeUnLock]; }); //讀 for (int i = 0; i < 10; i++) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self readLock]; NSLog(@"我開始讀%d",i); for (int j = 0; j < i; j++) { } NSLog(@"我讀完了%d",i); [self readUnLock]; }); } }
打印如下
可以看待寫入過程是完整的一個打印順序,而讀取過程由于沒有鎖的保護并沒有按順序執行。
一個簡單的 讀寫鎖 就完成了。代碼拙劣,大神勿笑。
總結
到此這篇關于iOS中讀寫鎖的簡單實現的文章就介紹到這了,更多相關iOS讀寫鎖實現內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://juejin.cn/post/7025421259231985677