iOS開發進階:鎖的分析

我們在使用多線程的時候,同一時刻可能會有多個線程訪問同一內存的內容,這樣就很容易引發數據混亂(數據安全)的問題。爲了減少或者避免這種問題的出現,我們需要使用鎖來保證統一時刻只有一個線程訪問這一塊內存。鎖可以讓數據的訪問更安全。

我們常見的鎖包括OSSpinLockdispatch_semaphore_tos_unfair_lockpthread_mutex_tNSlockNSConditionpthread_mutex_t(recursive)NSRecursiveLockNSConditionLock@synchronized等10來種。

按照特性劃分,鎖可以分爲自旋鎖互斥鎖:
1.自旋鎖:線程反覆檢查鎖變量是否可用。由於線程在這一過程中保持執行,因此是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,甚至顯示釋放自旋鎖。自旋鎖避免了進程上下文調度的開銷,因此對於線程只會阻塞很短時間的場合是有效的。
2.互斥鎖:是一種利用於多線程編程中,防止兩條線程同時對統一資源進行讀寫的機制。該目的是通過將代碼切片成一個一個臨時區二達成的。屬於互斥鎖的有NSlockpthread_mutex_t@synchronized

那麼它們各自的性能優勢什麼樣的呢?我們來做一個簡單的測試,測試代碼如下:

- (void)test{
    int loop = 100000;
    
    {
        
        OSSpinLock lock = OS_SPINLOCK_INIT;
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            OSSpinLockLock(&lock);
            OSSpinLockUnlock(&lock);
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"OSSpinLock:%f", (end - start)*1000);
    }
    
    {
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            dispatch_semaphore_signal(semaphore);
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"dispatch_semaphore_t:%f", (end - start)*1000);
    }
    
    {
        os_unfair_lock unfair = OS_UNFAIR_LOCK_INIT;
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            os_unfair_lock_lock(&unfair);
            os_unfair_lock_unlock(&unfair);
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"os_unfair_lock:%f", (end - start)*1000);
    }
    
    {
        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            pthread_mutex_lock(&mutex);
            pthread_mutex_unlock(&mutex);
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"pthread_mutex_t:%f", (end - start)*1000);
    }
    
    {
        pthread_mutex_t recurive;
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&recurive, &attr);
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            pthread_mutex_lock(&recurive);
            pthread_mutex_unlock(&recurive);
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"pthread_mutex_t(recurive):%f", (end - start)*1000);
    }
    
    {
        NSLock *lock = [NSLock new];
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            [lock lock];
            [lock unlock];
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"NSLock:%f", (end - start)*1000);
    }
    
    {
        NSRecursiveLock *recursiveLock = [NSRecursiveLock new];
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            [recursiveLock lock];
            [recursiveLock unlock];
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"NSRecursiveLock:%f", (end - start)*1000);
    }
    
    {
        NSCondition *condition = [NSCondition new];
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            [condition lock];
            [condition unlock];
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"NSCondition:%f", (end - start)*1000);
    }
    
    {
        NSConditionLock *conditionLock = [NSConditionLock new];
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            [conditionLock lock];
            [conditionLock unlock];
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"NSConditionLock:%f", (end - start)*1000);
    }
    
    {
        double_t start = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < loop; i++){
            @synchronized (self) {
                
            }
        }
        double_t end = CFAbsoluteTimeGetCurrent();
        NSLog(@"@synchronized:%f", (end - start)*1000);
    }
}

也就是我們嘗試加鎖解鎖10萬次,看這個過程耗費的時間(單位:毫秒ms)。
在模擬器上運行時間如下:(單位:毫秒)


在真機上運行時間如下:(單位:毫秒)


真機和模擬器上運行結果基本相近,在真機上@synchronized的表現要比模擬器上號很多,從7.5毫秒提升到2.7毫秒(猜測:@synchronized可能蘋果針對arm有專門的優化?)

1.@synchronized

加鎖的效果
遞歸可重用
結構

const NSString *syncKey = (NSString *)&__NSConstantStringImpl__var_folders_ns_73tnh7591jvg16yqm8fn6ykh0000gn_T_NXXcrun_0a2d9a_mi_0;

// @implementation NXXcrun
static void _C_NXXcrun_main(Class self, SEL _cmd) {
    {
        id _rethrow = 0;
        id _sync_obj = (id)syncKey;
        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);}
    }
}
// @end
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章