鎖的原理(一):@synchronized

一挟鸠、性能分析

網(wǎng)上很多對比八大鎖性能的文章,時間大部分比較早樱调。蘋果對某些鎖內(nèi)部進(jìn)行了優(yōu)化惜傲。這篇文章找中會以10萬次數(shù)據(jù)做對比對主流鎖性能進(jìn)行分析耘戚。

1.1 調(diào)用情況模擬

OSSpinLock
OSSpinLockiOS 10以后廢棄了,不過還可以調(diào)用操漠。需要導(dǎo)入頭文件<libkern/OSAtomic.h>

int hp_runTimes = 100000;
/** OSSpinLock 性能 */
{
    OSSpinLock hp_spinlock = OS_SPINLOCK_INIT;
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        OSSpinLockLock(&hp_spinlock);//解鎖
        OSSpinLockUnlock(&hp_spinlock);
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("OSSpinLock: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

dispatch_semaphore_t
信號量是GCD提供的:

/** dispatch_semaphore_t 性能 */
{
    dispatch_semaphore_t hp_sem = dispatch_semaphore_create(1);
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        dispatch_semaphore_wait(hp_sem, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_signal(hp_sem);
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("dispatch_semaphore_t: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

os_unfair_lock
os_unfair_lockiOS10推出的新類型的鎖需要導(dǎo)入頭文件<os/lock.h>

/** os_unfair_lock_lock 性能 */
{
    os_unfair_lock hp_unfairlock = OS_UNFAIR_LOCK_INIT;
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        os_unfair_lock_lock(&hp_unfairlock);
        os_unfair_lock_unlock(&hp_unfairlock);
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent() ;
    printf("os_unfair_lock_lock: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

pthread_mutex_t
pthread_mutex_tlinux下提供的鎖,需要導(dǎo)入頭文件<pthread/pthread.h>:

/** pthread_mutex_t 性能 */
{
    pthread_mutex_t hp_metext = PTHREAD_MUTEX_INITIALIZER;
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        pthread_mutex_lock(&hp_metext);
        pthread_mutex_unlock(&hp_metext);
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("pthread_mutex_t: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

NSLock
NSLockFoundation框架提供的鎖:

/** NSlock 性能 */
{
    NSLock *hp_lock = [NSLock new];
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        [hp_lock lock];
        [hp_lock unlock];
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("NSlock: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

NSCondition

/** NSCondition 性能 */
{
    NSCondition *hp_condition = [NSCondition new];
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        [hp_condition lock];
        [hp_condition unlock];
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("NSCondition: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

pthread_mutex_t(recursive)

/** PTHREAD_MUTEX_RECURSIVE 性能 */
{
    pthread_mutex_t hp_metext_recurive;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init (&attr);
    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init (&hp_metext_recurive, &attr);
    
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        pthread_mutex_lock(&hp_metext_recurive);
        pthread_mutex_unlock(&hp_metext_recurive);
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("PTHREAD_MUTEX_RECURSIVE: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

NSRecursiveLock

/** NSRecursiveLock 性能 */
{
    NSRecursiveLock *hp_recursiveLock = [NSRecursiveLock new];
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        [hp_recursiveLock lock];
        [hp_recursiveLock unlock];
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("NSRecursiveLock: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

NSConditionLock

/** NSConditionLock 性能 */
{
    NSConditionLock *hp_conditionLock = [NSConditionLock new];
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        [hp_conditionLock lock];
        [hp_conditionLock unlock];
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent() ;
    printf("NSConditionLock: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

@synchronized

/** @synchronized 性能 */
{
    double_t hp_beginTime = CFAbsoluteTimeGetCurrent();
    for (int i = 0 ; i < hp_runTimes; i++) {
        @synchronized(self) {}
    }
    double_t hp_endTime = CFAbsoluteTimeGetCurrent();
    printf("@synchronized: %f ms\n",(hp_endTime - hp_beginTime) * 1000);
}

鎖內(nèi)部沒有處理任何邏輯,都執(zhí)行的空操作浊伙,在10萬次循環(huán)后計算時間差值撞秋。

1.2 驗(yàn)證

iPhone 12 pro max 14.3真機(jī)測試數(shù)據(jù)如下:

OSSpinLock: 1.366019 ms
dispatch_semaphore_t: 1.923084 ms
os_unfair_lock_lock: 1.502037 ms
pthread_mutex_t: 1.694918 ms
NSlock: 2.384901 ms
NSCondition: 2.082944 ms
PTHREAD_MUTEX_RECURSIVE: 3.449082 ms
NSRecursiveLock: 3.075957 ms
NSConditionLock: 7.895947 ms
@synchronized: 3.794074 ms

iPhone 12 pro max 14.3模擬器測試數(shù)據(jù)如下:

OSSpinLock: 1.199007 ms
dispatch_semaphore_t: 1.991987 ms
os_unfair_lock_lock: 1.762986 ms
pthread_mutex_t: 2.611995 ms
NSlock: 2.719045 ms
NSCondition: 2.544045 ms
PTHREAD_MUTEX_RECURSIVE: 4.145026 ms
NSRecursiveLock: 5.039096 ms
NSConditionLock: 8.215070 ms
@synchronized: 10.205030 ms

對比如下:


鎖性能對比表

大部分鎖在真機(jī)上性能表現(xiàn)更好,@synchronized在真機(jī)與模擬器中表現(xiàn)差異巨大嚣鄙。也就是說蘋果在真機(jī)模式下優(yōu)化了@synchronized的性能吻贿。與之前相比目前@synchronized的性能基本能滿足要求。

判斷一把鎖的性能好壞哑子,一般情況下是與pthread_mutex_t做對比(因?yàn)榈讓佣际菍λ姆庋b)舅列。

二、@synchronized

由于@synchronized使用比較簡單卧蜓,并且目前真機(jī)性能也不錯帐要。所以先分析它。

2.1售票案例

有如下代碼:

@property (nonatomic, assign) NSUInteger ticketCount;

- (void)testTicket {
    self.ticketCount = 10;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 2; i++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 3; i++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

- (void)saleTicket {
    if (self.ticketCount > 0) {
        self.ticketCount--;
        sleep(0.1);
        NSLog(@"當(dāng)前余票還剩:%lu張",(unsigned long)self.ticketCount);
    } else {
        NSLog(@"當(dāng)前車票已售罄");
    }
}

模擬了多線程售票請款弥奸,輸出如下:

當(dāng)前余票還剩:6張
當(dāng)前余票還剩:7張
當(dāng)前余票還剩:7張
當(dāng)前余票還剩:7張
當(dāng)前余票還剩:4張
當(dāng)前余票還剩:4張
當(dāng)前余票還剩:3張
當(dāng)前余票還剩:2張
當(dāng)前余票還剩:1張
當(dāng)前余票還剩:0張
當(dāng)前車票已售罄
當(dāng)前車票已售罄
當(dāng)前車票已售罄
當(dāng)前車票已售罄
當(dāng)前車票已售罄

可以看到余票數(shù)量有重復(fù)以及順序混亂榨惠。
saleTicket加上@synchronized就能解決問題:

- (void)saleTicket {
    @synchronized(self) {
        if (self.ticketCount > 0) {
            self.ticketCount--;
            sleep(0.1);
            NSLog(@"當(dāng)前余票還剩:%lu張",(unsigned long)self.ticketCount);
        } else {
            NSLog(@"當(dāng)前車票已售罄");
        }
    }
}

一般參數(shù)傳遞self。那么有以下疑問:

  • 為什么要傳self呢盛霎?傳nil行不行赠橙?
  • @synchronized是怎么實(shí)現(xiàn)加鎖的效果的呢?
  • {}代碼塊究竟是什么呢愤炸?
  • 是否可以遞歸呢期揪?
  • 底層是什么數(shù)據(jù)結(jié)構(gòu)呢?

2.2 clang 分析 @synchronized

@synchronized是個系統(tǒng)關(guān)鍵字规个,那么通過clang還原它的底層實(shí)現(xiàn)凤薛,為了方便實(shí)現(xiàn)在main函數(shù)中調(diào)用它:

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        @synchronized(appDelegateClassName) {
            
        }
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

clang還原后代碼如下:

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    { __AtAutoreleasePool __autoreleasepool;
        appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
        {
            id _rethrow = 0;
            id _sync_obj = (id)appDelegateClassName;
            objc_sync_enter(_sync_obj);
            try {
                struct _SYNC_EXIT {
                    _SYNC_EXIT(id arg) : sync_exit(arg) {}
                    ~_SYNC_EXIT() {
                        objc_sync_exit(sync_exit);
                    }
                    id sync_exit;
                } _sync_exit(_sync_obj);
            }
            catch (id e) {
                _rethrow = e;
                
            }
            {
                struct _FIN {
                    _FIN(id reth) : rethrow(reth) {}
                    ~_FIN() {
                        if (rethrow) objc_exception_throw(rethrow);
                    }
                    id rethrow;
                } _fin_force_rethow(_rethrow);
                
            }
        }

    }
    return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}

異常處理不關(guān)心,所以核心就是try的邏輯绰姻,精簡后如下:

id _sync_obj = (id)appDelegateClassName;
objc_sync_enter(_sync_obj);
struct _SYNC_EXIT {
    _SYNC_EXIT(id arg) : sync_exit(arg) {}
    ~_SYNC_EXIT() {
        objc_sync_exit(sync_exit);
    }
    id sync_exit;
} _sync_exit(_sync_obj);

_SYNC_EXIT是個結(jié)構(gòu)體的定義枉侧,_sync_exit析構(gòu)的實(shí)現(xiàn)是objc_sync_exit(sync_exit),所以@synchronized本質(zhì)上等價于enter + exit

//@synchronized(appDelegateClassName) {}
//等價
objc_sync_enter(appDelegateClassName);
objc_sync_exit(appDelegateClassName);

它們是定義在objc中的狂芋。當(dāng)然也可以通過對@synchronized打斷點(diǎn)查看匯編定位:

image.png

2.3 源碼分析

2.3.1 objc_sync_enter

int objc_sync_enter(id obj)
{
    int result = OBJC_SYNC_SUCCESS;

    if (obj) {
        //obj存在的情況下 獲取 SyncData
        SyncData* data = id2data(obj, ACQUIRE);
        ASSERT(data);
        //加鎖
        data->mutex.lock();
    } else {
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        //不存在調(diào)用objc_sync_nil
        objc_sync_nil();
    }

    return result;
}
  • obj存在的情況下通過id2data獲取SyncData榨馁,參數(shù)是objACQUIRE
    • 然后通過mutex.lock()加鎖帜矾。
  • objnil的情況下調(diào)用objc_sync_nil翼虫,根據(jù)注釋does nothing是一個空實(shí)現(xiàn)。

mutex
mutexrecursive_mutex_t mutex類型屡萤,本質(zhì)上是recursive_mutex_tt

using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
class recursive_mutex_tt : nocopy_t {
    os_unfair_recursive_lock mLock;
    ......
}

typedef struct os_unfair_recursive_lock_s {
    os_unfair_lock ourl_lock;
    uint32_t ourl_count;
} os_unfair_recursive_lock, *os_unfair_recursive_lock_t;

os_unfair_recursive_lock是對os_unfair_lock的封裝珍剑。所以 @synchronized 是對os_unfair_lock 的封裝。

objc_sync_nil
objc_sync_nil的定義如下:

BREAKPOINT_FUNCTION(
    void objc_sync_nil(void)
);

#   define BREAKPOINT_FUNCTION(prototype)                             \
    OBJC_EXTERN __attribute__((noinline, used, visibility("hidden"))) \
    prototype { asm(""); }

替換還原后如下:

OBJC_EXTERN __attribute__((noinline, used, visibility("hidden"))) 
void objc_sync_nil(void) {
    asm(""); 
}

也就是一個空實(shí)現(xiàn)死陆。

2.3.2 objc_sync_exit

int objc_sync_exit(id obj)
{
    int result = OBJC_SYNC_SUCCESS;//0
    if (obj) {
        //獲取 SyncData
        SyncData* data = id2data(obj, RELEASE); 
        if (!data) {//沒有輸出返回錯誤code - 1
            result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
        } else {
            //獲取到數(shù)據(jù)先解鎖
            bool okay = data->mutex.tryUnlock();
            if (!okay) {//解鎖失敗返回-1
                result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
            }
        }
    } else {
        // @synchronized(nil) does nothing
    }

    return result;
}
  • obj存在的情況下通過id2data獲取SyncData招拙,參數(shù)是objRELEASE唧瘾。
  • 獲取到數(shù)據(jù)進(jìn)行解鎖,解鎖成功返回0别凤,失敗返回-1饰序。

2.3.3 SyncData 數(shù)據(jù)結(jié)構(gòu)

SyncData是一個結(jié)構(gòu)體:

typedef struct alignas(CacheLineSize) SyncData {
    struct SyncData* nextData;//下一個節(jié)點(diǎn)
    DisguisedPtr<objc_object> object;//obj,@synchronized的參數(shù)
    int32_t threadCount;  // number of THREADS using this block 線程數(shù)量
    recursive_mutex_t mutex;//鎖
} SyncData;
  • nextData指向下一個節(jié)點(diǎn),SyncData是一個單向鏈表规哪。
  • object存儲的是@synchronized的參數(shù)求豫,只不過進(jìn)行了包裝。
  • threadCount代表線程數(shù)量诉稍。支持多線程訪問蝠嘉。
  • mutex創(chuàng)建的鎖。遞歸鎖只能遞歸使用不能多線程使用杯巨。

三蚤告、id2data

objc_sync_enterobjc_sync_exit中都調(diào)用了id2data獲取數(shù)據(jù),區(qū)別是第二個參數(shù)舔箭,顯然id2data就是數(shù)據(jù)處理的核心了罩缴。

進(jìn)行代碼塊折疊后有如下邏輯:

image.png

syndata要么從TLS獲取,要么從cache獲取层扶。都沒有的情況下進(jìn)行創(chuàng)建箫章。

3.1 SyncData存儲結(jié)構(gòu)

#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap<SyncList> sDataLists;

//本身也是 os_unfair_lock
spinlock_t *lockp = &LOCK_FOR_OBJ(object);
SyncData **listp = &LIST_FOR_OBJ(object);
SyncData* result = NULL;

可以看到鎖和SyncData都是從sDataLists獲取的(hash map結(jié)構(gòu),存儲的是SyncList)镜会,SyncList定義如下:

struct SyncList {
    SyncData *data;
    spinlock_t lock;
    constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }
};

StripedMap定義如下:

class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    enum { StripeCount = 8 };
#else
    enum { StripeCount = 64 };
#endif

    struct PaddedT {
        T value alignas(CacheLineSize);
    };

    PaddedT array[StripeCount];
    ......
}

iOS真機(jī)上容量為8檬寂,其它平臺容量為64SynData根據(jù)前面的分析是一個單向鏈表戳表, 那么可以得到在哈希沖突的時候是采用拉鏈法解決的桶至。

增加以下驗(yàn)證代碼:

HPObject *obj = [HPObject alloc];
HPObject *obj2 = [HPObject alloc];
HPObject *obj3 = [HPObject alloc];
dispatch_async(dispatch_queue_create("HotpotCat", DISPATCH_QUEUE_CONCURRENT), ^{
    @synchronized (obj) {
        NSLog(@"obj");
        @synchronized (obj2) {
            NSLog(@"obj2");
            @synchronized (obj3) {
                NSLog(@"obj3");
            }
        }
    }
});

斷點(diǎn)驗(yàn)證:


image.png
  • sDataLists包裝了array,其中存儲的是SyncList集合匾旭,SyncListdata中存儲的是synData镣屹。

3.2 從 TLS 獲取 SyncData

  bool fastCacheOccupied = NO;//后續(xù)存儲的時候用
    //對 pthread_getspecific 的封裝,針對線程中第一次調(diào)用 @synchronized 是獲取不到數(shù)據(jù)的价涝。
    SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
    if (data) {
        fastCacheOccupied = YES;
        //判斷要查找的與存儲的object是不是同一個女蜈。
        if (data->object == object) {
            // Found a match in fast cache.
            uintptr_t lockCount;

            result = data;
            //獲取當(dāng)前線程對該對象鎖了幾次
            lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
            if (result->threadCount <= 0  ||  lockCount <= 0) {
                _objc_fatal("id2data fastcache is buggy");
            }

            switch(why) {
            case ACQUIRE: {//enter 的時候 lockCount + 1,并且存儲count到tls
                lockCount++;
                tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
                break;
            }
            case RELEASE: //exit的時候 lockCount - 1色瘩,并且存儲count到tls
                lockCount--;
                tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
                //當(dāng) count 減少到 0 的情況下清除對應(yīng)obj的SynData伪窖,這里并沒有清空count,count在存儲新objc的時候直接賦值為1
                if (lockCount == 0) {
                    // remove from fast cache
                    tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
                    // atomic because may collide with concurrent ACQUIRE
                    //threadCount - 1
                    OSAtomicDecrement32Barrier(&result->threadCount);
                }
                break;
            case CHECK:
                // do nothing
                break;
            }

            return result;
        }
    }
  • 通過tls_get_direct(是對_os_tsd_get_direct的封裝)獲取當(dāng)前線程存儲的SynData數(shù)據(jù)居兆。
  • 在數(shù)據(jù)存在的情況下判斷標(biāo)記fastCacheOccupied存在覆山。
  • 判斷tls存儲的數(shù)據(jù)是不是當(dāng)前對象。是當(dāng)前對象則進(jìn)行進(jìn)一步處理泥栖,否則結(jié)束tls邏輯簇宽。
  • 獲取對象加鎖的次數(shù)lockCount勋篓。
  • enter邏輯:lockCount++并存儲在tls
  • exit邏輯:lockCount--并存儲在tls晦毙。
    • 當(dāng)lockCount0的時候釋放SynData生巡,直接在tls中置為NULL
    • 并且threadCount - 1见妒。

線程局部存儲(Thread Local Storage,TLS): 是操作系統(tǒng)為線程單獨(dú)提供的私有空間,通常只有有限的容量甸陌。
Linux系統(tǒng)下通常通過pthread庫中的相關(guān)方法進(jìn)行操作:
pthread_key_create()须揣、
pthread_getspecific()
pthread_setspecific()钱豁、
pthread_key_delete()

3.3 從 Cache 獲取 SyncData

當(dāng)tls中沒有找到SynData的時候會去Cache中找:

    //獲取線程緩存耻卡,參數(shù)NO 當(dāng)緩存不存在的時候不進(jìn)行創(chuàng)建。
    SyncCache *cache = fetch_cache(NO);
    if (cache) {
        unsigned int i;
        for (i = 0; i < cache->used; i++) {
            SyncCacheItem *item = &cache->list[i];
            //找到obj對應(yīng)的 item
            if (item->data->object != object) continue;

            // Found a match.
            //獲取SynData
            result = item->data;
            if (result->threadCount <= 0  ||  item->lockCount <= 0) {
                _objc_fatal("id2data cache is buggy");
            }
                
            switch(why) {
            case ACQUIRE://enter lockCount + 1
                item->lockCount++;
                break;
            case RELEASE://exit lockCount - 1
                item->lockCount--;
                if (item->lockCount == 0) {//lockCount = 0 的時候 從cache中移除i的元素牲尺,將最后一個元素存儲到原先i的位置卵酪。used - 1。也就是最后一個位置被標(biāo)記為未使用了谤碳。
                    // remove from per-thread cache
                    cache->list[i] = cache->list[--cache->used];
                    // atomic because may collide with concurrent ACQUIRE
                    //threadCount - 1
                    OSAtomicDecrement32Barrier(&result->threadCount);
                }
                break;
            case CHECK:
                // do nothing
                break;
            }

            return result;
        }
    }
  • 通過fetch_cache(是對pthread_getspecific的封裝)找SyncCache溃卡,由于是讀取數(shù)據(jù),所以找不到的情況下這里不創(chuàng)建蜒简。
  • 遍歷cache已使用的空間找到obj對應(yīng)的SyncCacheItem瘸羡。
  • enter的情況下item->lockCount++
  • exit情況下item->lockCount--
    • 當(dāng)item->lockCount == 0的時候?qū)?code>cache中這個item替換為cache中最后一個搓茬,used -1標(biāo)記cache中使用的數(shù)量犹赖,這樣就將cache中數(shù)據(jù)釋放了。
    • syndatathreadCount進(jìn)行-1卷仑。

3.3.1 SyncCache

typedef struct {
    SyncData *data;//數(shù)據(jù)
    unsigned int lockCount;  // 被當(dāng)前線程加鎖次數(shù)
} SyncCacheItem;

typedef struct SyncCache {
    unsigned int allocated;//總?cè)萘?    unsigned int used;//已使用
    SyncCacheItem list[0];//列表
} SyncCache;
  • SyncCache中存儲的是SyncCacheItem的一個list峻村,allocated用于記錄開辟的總?cè)萘浚?code>used記錄已經(jīng)使用的容量。
  • SyncCacheItem存儲了一個SyncData以及lockCount锡凝。記錄的是針對當(dāng)前線程SyncData被鎖了多少次粘昨。SyncCacheItem存儲的對應(yīng)于TSL快速緩存的SYNC_COUNT_DIRECT_KEYSYNC_DATA_DIRECT_KEY

3.3.2 fetch_cache

static SyncCache *fetch_cache(bool create)
{
    _objc_pthread_data *data;
    //creat用來處理是否新建私爷。
    data = _objc_fetch_pthread_data(create);
    //data不存在直接返回雾棺,create為YES的情況下data不會為空
    if (!data) return NULL;
    //syncCache不存在
    if (!data->syncCache) {
        if (!create) {//不允許創(chuàng)建直接返回 NULL
            return NULL;
        } else {
            //允許創(chuàng)建直接 calloc 創(chuàng)建,初始容量為4.
            int count = 4;
            data->syncCache = (SyncCache *)
                calloc(1, sizeof(SyncCache) + count*sizeof(SyncCacheItem));
            data->syncCache->allocated = count;
        }
    }

    // Make sure there's at least one open slot in the list.
    //存滿的情況下擴(kuò)容 2倍擴(kuò)容衬浑。
    if (data->syncCache->allocated == data->syncCache->used) {
        data->syncCache->allocated *= 2;
        data->syncCache = (SyncCache *)
            realloc(data->syncCache, sizeof(SyncCache) 
                    + data->syncCache->allocated * sizeof(SyncCacheItem));
    }

    return data->syncCache;
}
  • 通過_objc_fetch_pthread_data獲取_objc_pthread_data捌浩,_objc_pthread_data存儲了SyncCache信息,當(dāng)然不僅僅是它:
    image.png
  • data不存在直接返回工秩,createYES的情況下data不會為空尸饺。
  • syncCache不存在的情況下进统,允許創(chuàng)建則進(jìn)行calloc(初始容量4,這里是創(chuàng)建syncCache)浪听,否則返回NULL螟碎。
  • syncCache存滿(通過allocatedused判斷)的情況下進(jìn)行2被擴(kuò)容。

_objc_fetch_pthread_data

_objc_pthread_data *_objc_fetch_pthread_data(bool create)
{
    _objc_pthread_data *data;
    //pthread_getspecific TLS_DIRECT_KEY
    data = (_objc_pthread_data *)tls_get(_objc_pthread_key);
    if (!data  &&  create) {
        //允許創(chuàng)建的的情況下創(chuàng)建
        data = (_objc_pthread_data *)
            calloc(1, sizeof(_objc_pthread_data));
        //保存
        tls_set(_objc_pthread_key, data);
    }

    return data;
}
  • 通過tls_get獲取_objc_pthread_data迹栓,不存在并且允許創(chuàng)建的情況下進(jìn)行calloc創(chuàng)建_objc_pthread_data掉分。
  • 創(chuàng)建后保存到tls

這里的cache也是存儲在tls克伊,與tls_get_direct的區(qū)別要看二者存取的邏輯酥郭,一個調(diào)用的是tls_get_direct,一個是tls_get

#if defined(__PTK_FRAMEWORK_OBJC_KEY0)
#   define SUPPORT_DIRECT_THREAD_KEYS 1
#   define TLS_DIRECT_KEY        ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY0)
#   define SYNC_DATA_DIRECT_KEY  ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY1)
#   define SYNC_COUNT_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY2)
#   define AUTORELEASE_POOL_KEY  ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY3)
# if SUPPORT_RETURN_AUTORELEASE
#   define RETURN_DISPOSITION_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY4)
# endif
#else
#   define SUPPORT_DIRECT_THREAD_KEYS 0
#endif

#if SUPPORT_DIRECT_THREAD_KEYS
#define _objc_pthread_key TLS_DIRECT_KEY
#else
static tls_key_t _objc_pthread_key;
#endif

//key _objc_pthread_key
static inline void *tls_get(tls_key_t k) { 
    return pthread_getspecific(k);
}

//key SYNC_DATA_DIRECT_KEY 與  SYNC_COUNT_DIRECT_KEY
static inline void *tls_get_direct(tls_key_t k)
{ 
    ASSERT(is_valid_direct_key(k));

    if (_pthread_has_direct_tsd()) {
        return _pthread_getspecific_direct(k);
    } else {
        return pthread_getspecific(k);
    }
}

__header_always_inline int
_pthread_has_direct_tsd(void)
{
#if TARGET_IPHONE_SIMULATOR
    return 0;
#else
    return 1;
#endif
}

__header_always_inline void *
_pthread_getspecific_direct(unsigned long slot)
{
#if TARGET_IPHONE_SIMULATOR
    return pthread_getspecific(slot);
#else
    return _os_tsd_get_direct(slot);
#endif
}

__attribute__((always_inline))
static __inline__ void*
_os_tsd_get_direct(unsigned long slot)
{
    return _os_tsd_get_base()[slot];
}
  • _objc_pthread_data通過pthread_getspecific獲取緩存數(shù)據(jù)愿吹,key的類型是tls_key_t
    • 如果支持SUPPORT_DIRECT_THREAD_KEYS不从,key__PTK_FRAMEWORK_OBJC_KEY0
    • 不支持SUPPORT_DIRECT_THREAD_KEYS犁跪,key_objc_pthread_key椿息。
  • TLS快速緩存通過tls_get_direct獲取,keytls_key_t類型坷衍。
    • SynData對應(yīng)的key__PTK_FRAMEWORK_OBJC_KEY1寝优。
    • lockCount對應(yīng)的key__PTK_FRAMEWORK_OBJC_KEY2
    • iOS模擬器通過pthread_getspecific獲取
    • 其它通過_os_tsd_get_direct獲取惫叛,調(diào)用的是_os_tsd_get_base()倡勇,不同架構(gòu)對應(yīng)不同匯編指令:
__attribute__((always_inline, pure))
static __inline__ void**
_os_tsd_get_base(void)
{
#if defined(__arm__)
    uintptr_t tsd;
    __asm__("mrc p15, 0, %0, c13, c0, 3\n"
                "bic %0, %0, #0x3\n" : "=r" (tsd));
    /* lower 2-bits contain CPU number */
#elif defined(__arm64__)
    uint64_t tsd;
    __asm__("mrs %0, TPIDRRO_EL0\n"
                "bic %0, %0, #0x7\n" : "=r" (tsd));
    /* lower 3-bits contain CPU number */
#endif

    return (void**)(uintptr_t)tsd;
}

3.4 從sDataLists獲取SynData

    //sDataLists 中找 Syndata
    {
        SyncData* p;
        SyncData* firstUnused = NULL;
        //從SynList鏈表中查找SynData
        for (p = *listp; p != NULL; p = p->nextData) {
            if ( p->object == object ) {
                result = p;//找到
                // atomic because may collide with concurrent RELEASE
                //threadCount + 1,由于在上面線程緩存和tls的查找中沒有找到嘉涌,但是在 sDataLists 中找到了妻熊。所以肯定不是同一個線程了(那也肯定就不是exit,而是enter了)仑最,線程數(shù)量+1扔役。
                OSAtomicIncrement32Barrier(&result->threadCount);
                goto done;
            }
            //沒有找到的情況下找到了空位。
            if ( (firstUnused == NULL) && (p->threadCount == 0) )
                firstUnused = p;
        }
    
        // no SyncData currently associated with object
        //是exit就直接跳轉(zhuǎn)到done的邏輯
        if ( (why == RELEASE) || (why == CHECK) )
            goto done;
    
        // an unused one was found, use it
        //找到一個未使用的(也有可能是之前使用過警医,threadCount現(xiàn)在變?yōu)?了)亿胸,直接存儲當(dāng)前objc數(shù)據(jù)(這里相當(dāng)于釋放了sDataLists中的舊數(shù)據(jù))。
        if ( firstUnused != NULL ) {
            result = firstUnused;
            //替換object
            result->object = (objc_object *)object;
            result->threadCount = 1;
            goto done;
        }
    }
  • 遍歷開始獲取的SynListobj對應(yīng)的SynData预皇。
  • 找到的情況下threadCount + 1侈玄,由于在tls(快速以及cache中)沒有找到數(shù)據(jù),但是在sDataLists中找到了吟温,所以肯定不在同一個線程(那也肯定就不是exit序仙,而是enter了)直接跳轉(zhuǎn)到done
  • eixt的邏輯直接跳轉(zhuǎn)到done鲁豪。
  • 沒有找到但是找到了threadCount = 0Syndata潘悼,也就是找到了空位(之前使用過律秃,threadCount現(xiàn)在變?yōu)?code>0了)。
    • 直接存儲當(dāng)前objc數(shù)據(jù)到synData中(這里相當(dāng)于釋放了sDataLists中的舊數(shù)據(jù))治唤。threadCount標(biāo)記為1棒动。

3.5 創(chuàng)建 SyncData

當(dāng)tls中沒有快速緩存、也沒cache宾添、并且sDataLists中沒有數(shù)據(jù)也沒有空位:

posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData));
//對象本身
result->object = (objc_object *)object;
//持有線程數(shù)初始化為1
result->threadCount = 1;
//創(chuàng)建鎖
new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);
//頭插法
result->nextData = *listp;
//這里 sDataLists 中的 SynList就賦值了船惨。
*listp = result;
  • 開辟一個SyncData大小的內(nèi)存并進(jìn)行對齊。
  • 設(shè)置object以及threadCount缕陕。
  • 創(chuàng)建mutex鎖掷漱。
  • 頭插法將創(chuàng)建的SynData插入SynList中。也就相當(dāng)于將數(shù)據(jù)存入sDataLists中榄檬。nextData存在的情況是發(fā)生了哈希沖突。

3.6 done 緩存存儲邏輯

    //數(shù)據(jù)存儲
    if (result) {//有result衔统,無論是創(chuàng)建的還是從 sDataLists 獲取的鹿榜。
        // Only new ACQUIRE should get here.
        // All RELEASE and CHECK and recursive ACQUIRE are 
        // handled by the per-thread caches above.
        if (why == RELEASE) {//exit不進(jìn)行任何操作
            // Probably some thread is incorrectly exiting 
            // while the object is held by another thread.
            return nil;
        }
        if (why != ACQUIRE) _objc_fatal("id2data is buggy");
        if (result->object != object) _objc_fatal("id2data is buggy");

#if SUPPORT_DIRECT_THREAD_KEYS
        //TLS 快速緩存不存在,存儲到快速緩存锦爵。
        if (!fastCacheOccupied) {//
            // Save in fast thread cache
            //存儲Syndata
            tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
            //存儲count為1
            tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);
        } else 
#endif
        //cache存儲 不支持 tls 快速緩存 或者 tls快速緩存存在的情況下
        {
            // Save in thread cache
            //獲取SyncCache舱殿,不存在的時候進(jìn)行創(chuàng)建
            if (!cache) cache = fetch_cache(YES);
            //將result放入list的最后一個元素,SyncCacheItem 中存儲 result 以及 lockCount
            cache->list[cache->used].data = result;
            cache->list[cache->used].lockCount = 1;
            cache->used++;
        }
    }
  • exit的時候不進(jìn)行任何操作:
    • TLS快速緩存會在獲取緩存的時候進(jìn)行釋放险掀。并且threadCount -1沪袭。
    • cache邏輯會進(jìn)行替換數(shù)據(jù)(相當(dāng)于釋放),并且threadCount -1樟氢。
    • sDataLists獲取數(shù)據(jù)邏輯本身不釋放冈绊,會根據(jù)threadCount = 0找到空位進(jìn)行替換,相當(dāng)于釋放埠啃。
  • 在支持快速緩存并且快速緩存不存在的情況下死宣,將創(chuàng)建的SynData以及lockCount = 1存儲到TLS快速緩存中。
  • 在不支持快速緩存或者快速緩存已經(jīng)有值了的情況下將SynData構(gòu)造SyncCacheItem存入SyncCache中碴开。
  • 也就是說SynData只會在快速緩存與Cache中存在一個毅该,同時會存儲在sDataLists中。

3.7 驗(yàn)證

3.7.1 @synchronized 數(shù)據(jù)結(jié)構(gòu)

根據(jù)源碼分析@synchronized數(shù)據(jù)結(jié)構(gòu)如下:

@synchronized數(shù)據(jù)存儲結(jié)構(gòu)

3.7.2 驗(yàn)證

有如下代碼:

HPObject *obj = [HPObject alloc];
HPObject *obj2 = [HPObject alloc];
HPObject *obj3 = [HPObject alloc];
dispatch_async(dispatch_queue_create("HotpotCat", DISPATCH_QUEUE_CONCURRENT), ^{
    @synchronized (obj) {
        @synchronized (obj) {
            @synchronized (obj) {
                //obj lockCount = 3 threadCount = 1
                NSLog(@"1 = %p",obj);
                @synchronized (obj2) {
                    //obj2 lockCount = 1 threadCount = 1潦牛,有可能存在拉鏈
                    NSLog(@"2 = %p",obj2);
                    @synchronized (obj3) {
                        //obj3 threadCount = 1, lockCount = 1眶掌,必然存在拉鏈(為了方便驗(yàn)證源碼強(qiáng)制修改StripeCount為2)
                        NSLog(@"3 = %p",obj3);
                        dispatch_async(dispatch_queue_create("HotpotCat1", DISPATCH_QUEUE_CONCURRENT), ^{
                            @synchronized (obj) {
                                //obj threadCount = 2,一個線程的 lockCount = 3 另外一個 lockCount = 1
                                NSLog(@"4 = %p",obj);
                            }
                        });
                        //為了讓 @synchronized 不exit
                        sleep(10);
                    }
                }
            }
        }
    }
});

do {
    
} while (1);

由于源碼是mac工程,在main函數(shù)中寫一個死循環(huán)巴碗。為了方便驗(yàn)證將源碼中StripeCount改為2

image.png

NSLog@synchronized處斷點(diǎn)驗(yàn)證朴爬。

  • 1處的驗(yàn)證結(jié)果:

    image.png

    lockCount = 3threadCount = 1良价,并且sDataLists中存儲的與快速緩存中是同一個SynData地址寝殴。符合預(yù)期蒿叠。

  • 2處驗(yàn)證結(jié)果:

    image.png

可以看到這個時候第二個元素已經(jīng)進(jìn)行了拉鏈,并且obj2在鏈表的頭結(jié)點(diǎn)蚣常。

  • 3處結(jié)果驗(yàn)證:
    image.png

仍然進(jìn)行了拉鏈obj3 -> obj2 -> obj市咽。

  • 4處驗(yàn)證結(jié)果:
    image.png

這個時候obj對應(yīng)的SynDatathreadCount2了。

所有驗(yàn)證結(jié)果符合分析預(yù)期抵蚊。

四施绎、總結(jié)

  • 參數(shù)傳nil沒有做任何事情。傳self在使用過程中不會被釋放贞绳,并且同一個類中如果都用self底層只會存在一個SynData谷醉。

  • @synchronized底層是封裝的os_unfair_lock

  • objc_sync_enter中加鎖冈闭,objc_sync_exit中解鎖俱尼。

  • @synchronized加鎖的數(shù)據(jù)信息都存儲在sDataLists全局哈希表中。同時還有TLS快速緩存(一個SynData數(shù)據(jù)萎攒,通常是第一個遇八,釋放后會存放新的)以及線程緩存(一組SyncData數(shù)據(jù))。這兩個緩存互斥耍休,同一個SyncData只存在其中一個)

  • id2data獲取SynData流程:

    • TLS快速緩存獲热杏馈(SYNC_COUNT_DIRECT_KEY),obj對應(yīng)的SyncData存在的情況下獲取SYNC_COUNT_DIRECT_KEY對應(yīng)的lockCount羊精。
      • enterlockCount++并存儲到SYNC_COUNT_DIRECT_KEY斯够。
      • exitlockCount--并存儲到SYNC_COUNT_DIRECT_KEYlockCount == 0清空SYNC_DATA_DIRECT_KEY喧锦,threadCount -1读规。
    • TLS cache緩存獲取,遍歷cache找到對應(yīng)的SyncData裸违。
      • enterlockCount++掖桦。
      • exitlockCount--lockCount == 0替換cache->list對應(yīng)的值為最后一個供汛,used -1枪汪,threadCount -1
    • sDataLists全局哈希表獲取SyncData:找到的情況下threadCount + 1進(jìn)入緩存邏輯怔昨,沒有找到并且存在threadCount = 0則替換object相當(dāng)于存儲了新值雀久。
    • SyncData創(chuàng)建:創(chuàng)建SyncData,賦值object趁舀,threadCount初始化為1赖捌,創(chuàng)建mutex鎖。并且采用頭插法將SyncData插入sDataLists對應(yīng)的SynList頭部。
    • SyncData數(shù)據(jù)緩存:sDataLists添加了或者更新了數(shù)據(jù)會走到緩存邏輯越庇,緩存邏輯是往TLS快速緩存以及TLS cache緩存添加數(shù)據(jù)
      • enterTLS快速緩存不存在的情況下將SyncData存儲快速緩存罩锐,否則存入cache緩存的尾部。
      • exit:直接return卤唉。
  • lockCount是針對單個線程而言的涩惑,當(dāng)lockCount = 0的時候?qū)?shù)據(jù)進(jìn)行釋放

    • TLS快速緩存是直接設(shè)置為NULL(只有一個SyncData
    • TLS cache緩存是直接用最后一個數(shù)據(jù)進(jìn)行替換(一組SyncData)桑驱,然后used -1進(jìn)行釋放竭恬。
    • 同時threadCount - 1相當(dāng)于當(dāng)前線程釋放。
  • threadCount是針對跨線程的熬的,在threadCount = 0的時候并不立即釋放痊硕,而是在下次插入數(shù)據(jù)的時候進(jìn)行替換。sDataLists保存所有的數(shù)據(jù)押框。

  • lockCount@synchronized可重入可遞歸的原因岔绸,threadCount@synchronized可跨線程的原因。

@synchronized數(shù)據(jù)之間關(guān)系:

@synchronized數(shù)據(jù)關(guān)聯(lián)關(guān)系

@synchronized完整調(diào)用流程:

@synchronized流程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末橡伞,一起剝皮案震驚了整個濱河市亭螟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骑歹,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件墨微,死亡現(xiàn)場離奇詭異道媚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翘县,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門最域,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锈麸,你說我怎么就攤上這事镀脂。” “怎么了忘伞?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵薄翅,是天一觀的道長。 經(jīng)常有香客問我氓奈,道長翘魄,這世上最難降的妖魔是什么簇抵? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任梆惯,我火速辦了婚禮璃氢,結(jié)果婚禮上讽膏,老公的妹妹穿的比我還像新娘钢猛。我一直安慰自己,他們只是感情好娜谊,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布彼城。 她就那樣靜靜地躺著,像睡著了一般腹躁。 火紅的嫁衣襯著肌膚如雪桑包。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天潜慎,我揣著相機(jī)與錄音捡多,去河邊找鬼。 笑死铐炫,一個胖子當(dāng)著我的面吹牛垒手,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播倒信,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼科贬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鳖悠?” 一聲冷哼從身側(cè)響起榜掌,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乘综,沒想到半個月后憎账,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卡辰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年胞皱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片九妈。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡反砌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萌朱,到底是詐尸還是另有隱情宴树,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布晶疼,位于F島的核電站酒贬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏翠霍。R本人自食惡果不足惜同衣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壶运。 院中可真熱鬧耐齐,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辕翰,卻和暖如春夺衍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喜命。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工沟沙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壁榕。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓矛紫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親牌里。 傳聞我的和親對象是個殘疾皇子颊咬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 鎖的種類 借用網(wǎng)上的一張有關(guān)鎖性能的對比圖,如下所示: 從上圖中我們可以看出來牡辽,鎖大概可以分為以下幾種: 1.:在...
    含笑州閱讀 1,008評論 0 0
  • 1喳篇、八大鎖效率 八大鎖分別:自璇所:OSSpinLock。在iOS10以后該鎖被重寫态辛,會在堵塞時進(jìn)行休眠麸澜;互斥鎖:...
    Henry________閱讀 805評論 0 4
  • 1、鎖的歸類 鎖的分類只有兩大類自旋鎖和和互斥鎖奏黑。這兩大類下又分成很多不同的小類痰憎。了解鎖之前建議先了解一下線程及線...
    希爾羅斯沃德_董閱讀 1,982評論 2 4
  • iOS之武功秘籍 文章匯總[http://www.reibang.com/p/07991e5b1c30] 寫在前...
    長茳閱讀 604評論 0 2
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險厭惡者,不喜歡去冒險攀涵,但是人生放棄了冒險,也就放棄了無數(shù)的可能洽沟。 ...
    yichen大刀閱讀 6,059評論 0 4