iOS多線程之@synchronized探索

iOS多線程可能形成共享資源的競爭,使用鎖能夠很好的解決這一問題,iOS的鎖有不少種,從性能上看@synchronized彷佛沒啥競爭力,來自# 再也不安全的 OSSpinLockios

lock_benchmark.png

可是咱們爲何還要用這廝呢,由於用起來簡單啊!!!!git

    @synchronized (person) {
        //do something
    }
複製代碼

源碼分析

打開always show disassembly運行代碼github

image.png

image.png

經過objc_retain和objc_release能夠看到@synchronized和對象強引用,驗證一下數組

int main(int argc, char * argv[]) {

    JPerson *person = [[JPerson alloc] init];
    NSLog(@"--%lu",CFGetRetainCount((__bridge CFTypeRef)(person)));
    @synchronized (person) {
        NSLog(@"%@",person);
        NSLog(@"--%lu",CFGetRetainCount((__bridge CFTypeRef)(person)));
    }
    NSLog(@"--%lu",CFGetRetainCount((__bridge CFTypeRef)(person)));
    return 0;
}
複製代碼

image.png

objc_sync_enter

objc4-781.2搜索objc_sync_enter緩存

image.png

  • 若是obc爲空就執行objc_sync_nil,進一步查看發現什麼也沒執行
  • obc不爲空就獲取對象SyncData* datadata->mutext加鎖

這裏的重點應該在SyncDataid2datasass

SyncData

image.png

這是一個單鏈表節點安全

  • nextData:指向下一個節點的指針
  • object:包裝後的obj
  • threadCount:有多少個線程訪問obj
  • mutex:recursive_mutext_t類型的遞歸互斥鎖

id2data

image.png

這裏代碼比較多,關注點太多容易分散精力,咱們只重點關注非緩存的狀況系統是如何處理的markdown

image.png

點進去看看多線程

image.png

這時的重點來到了StripedMap<SyncList>app

image.png

image.png

能夠看到StripedMap是一個數組結構,用來存儲SyncList

image.png

  • 若是找到了obj對應的SyncData跳轉到done執行
  • 若是沒找到obj對應的SyncData可是找到了空節點,那麼將obj賦值給空節點
  • 若是都沒找到,那麼建立一個插入SyncList的頭部

image.png

id2data簡單總結

0b265c3307354cb9a79a75638462335f_tplv-k3u1fbpfcp-watermark.png

如圖:有一個全局的長度爲8的數組StripedMap(這個數組是不須要擴容的),數組裏存儲的是SyncList指針,SyncList裏面存儲SyncData節點指針,SyncData節點存儲指向下一個SyncData節點指針,造成單鏈表結構。

爲了提高訪問速度,蘋果設計了兩級緩存TLSSyncCache,下面咱們具體看一下

TLS(Thread Local Storage)線程本地存儲

TLS能夠認爲是線程私有存儲空間

image.png

  • tls_get_direct:根據key從TLS字典讀取值
  • tls_set_direct:根據key、value存儲到TLS字典

image.png

能夠看到若是沒有從快速緩存TLS中查找到結果,done後面代碼執行也會存儲一份進去

SyncCache

image.png

操做很簡單,就是遍歷數組匹配object

image.png

  • SyncCacheItem結構體包含
    • SyncData指針
    • 當前線程訪問次數lockCount
  • SyncCache結構體包含
    • 存儲SyncCacheItem的數組list
    • 數組長度allocated
    • 當前已經存儲元素數量used

fetch_cache

image.png

_objc_pthread_data結構

image.png

_objc_fetch_pthread_data

image.png

從這裏咱們看到了兩個熟悉的身影tls_gettls_set,建立的_objc_pthread_data結構體也是存儲在了TLS中

image.png

objc_sync_exit

image.png

只作了一個解鎖操做,建立的SyncData並無刪除

注意

咱們是經過傳入的obj地址做爲標識來操做SyncData的,若是obj地址發生變化那是會出現問題的,例如

image.png

簡單理解假如t一、t2兩個線程要訪問personperson初始值爲a0

  • t1獲取到person地址爲a0,加鎖
  • t2獲取到person地址也爲a0,等待解鎖
  • t1操做完以後person地址變爲a1,a0被釋放,解鎖,t2可訪問
  • t2操做完以後person地址變爲a2,a0又被釋放,重複釋放形成崩潰

因此咱們通常傳入對屬性所在對象的self,這樣雖然必定程度上保證了線程安全,可是影響範圍擴大

參考文章

拋開性能,談談不應用@Synchronized的緣由

iOS多線程鎖之@synchronized原理分析

IOS - @synchronized詳解

再也不安全的 OSSpinLock

相關文章
相關標籤/搜索