iOS -鎖-NSLock

NSLockiOS開發(fā)中最基礎(chǔ)的鎖。它繼承自NSObject,遵守NSLocking協(xié)議。用于處理線程安全問題。

下面我們來看一個例子:

for (int i = 0; i < 200000; i++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        self.mArray = [NSMutableArray array];
    });
}

運行該程序會崩潰,這是因為,我們在不斷地創(chuàng)建array,mArray在不斷的賦新值,釋放舊值,這個時候多線程操作就會可能存在值已經(jīng)被釋放了,而其他線程還在操作,此時就會發(fā)生崩潰。此時就需要我們對程序加鎖。將上述程序改成如下:

NSLock *lock = [[NSLock alloc] init];
for (int i = 0; i < 200000; i++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [lock lock];
        self.mArray = [NSMutableArray array];
        [lock unlock];
    });
}

程序就能正常運行了,這是因為此時,每一條線程執(zhí)行self.mArray = [NSMutableArray array]的前后,都會有獲取鎖釋放鎖的過程,此時這句代碼是在線程安全的情況下執(zhí)行的,所以并沒有異常問題。

那么NSLock到底做了什么?附上一份Objective-C的源碼:

@interface NSLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name;

@end

以上方法只能看到定義,并不能看到實現(xiàn),那我們再通過swift的開源代碼來看看:

open class NSLock: NSObject, NSLocking {
    internal var mutex = _MutexPointer.allocate(capacity: 1)
    private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
    private var timeoutMutex = _MutexPointer.allocate(capacity: 1)

    public override init() {
        pthread_mutex_init(mutex, nil)
        pthread_cond_init(timeoutCond, nil)
        pthread_mutex_init(timeoutMutex, nil)
    }
    
    deinit {
        pthread_mutex_destroy(mutex)
        mutex.deinitialize(count: 1)
        mutex.deallocate()
        deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
    }
    
    // 獲取鎖
    open func lock() {
        pthread_mutex_lock(mutex)
    }

    // 釋放鎖
    open func unlock() {
        pthread_mutex_unlock(mutex)

        // Wakeup any threads waiting in lock(before:)
        pthread_mutex_lock(timeoutMutex)
        pthread_cond_broadcast(timeoutCond)
        pthread_mutex_unlock(timeoutMutex)
    }
    
    // 嘗試加鎖,不會阻塞線程。true則加鎖成功。
    // false則失敗,說明其他線程在加鎖中這個方法無論如何都會立即返回。
    open func `try`() -> Bool {
        return pthread_mutex_trylock(mutex) == 0
    }
    
    // 嘗試在指定NSDate之前加鎖,會阻塞線程。true則加鎖成功。
    // false則失敗,說明其他線程在加鎖中這個方法無論如何都會立即返回。
    open func lock(before limit: Date) -> Bool {
        if pthread_mutex_trylock(mutex) == 0 {
            return true
        }
        return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
    }

    open var name: String?
}

從源碼上看NSLock其實只是對pthread_mutex做了一層簡單的封裝。它屬于互斥鎖的一種。當一個線程進行訪問的時候,該線程獲得鎖,其他線程進行訪問的時候,將被操作系統(tǒng)掛起,直到該線程釋放鎖,其他線程才能對其進行訪問,從而卻確保了線程安全。

- (void)nslockTest {
    NSLock *lock = [[NSLock alloc] init];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"進入線程1");
        sleep(2);
        [lock lock];
        NSLog(@"執(zhí)行任務(wù)1");
        [lock unlock];
        NSLog(@"退出線程1");
    });
        
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"進入線程2");
        [lock lock];
        sleep(5);
        NSLog(@"執(zhí)行任務(wù)2");
        [lock unlock];
        NSLog(@"退出線程2");
    });
}

運行程序,控制臺輸出:

image

通過結(jié)果,我們可以看到雖然程序先進入線程1,但是由于我們在執(zhí)行lock加入了延遲,由于是并發(fā)操作,所以緊接著,會進入線程2,線程2可以立即執(zhí)行lock操作,雖然我們緊接著sleep了5秒鐘,但是由于鎖已經(jīng)被線程2占用,并不會去執(zhí)行線程1的操作,此時線程1就被阻塞了,只有等到線程2執(zhí)行完成解鎖之后才會進入線程1執(zhí)行任務(wù)。這也就完美的體現(xiàn)了互斥鎖的特性。

需要注意的是,NSLock在使用不當?shù)臅r候會造成堵塞線程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 引用自多線程編程指南應(yīng)用程序里面多個線程的存在引發(fā)了多個執(zhí)行線程安全訪問資源的潛在問題。兩個線程同時修改同一資源有...
    Mitchell閱讀 2,108評論 1 7
  • 轉(zhuǎn)發(fā):LockForiOS 又到了春天挪坑的季節(jié),想起多次被問及到鎖的概念,決定好好總結(jié)一番。 翻看目前關(guān)于 iO...
  • 轉(zhuǎn)載:談 iOS 的鎖 又到了春天挪坑的季節(jié),想起多次被問及到鎖的概念,決定好好總結(jié)一番。 翻看目前關(guān)于 iOS ...
    小鯤鵬閱讀 587評論 0 0
  • 鎖是一種同步機制,用于多線程環(huán)境中對資源訪問的限制iOS中常見鎖的性能對比圖(摘自:ibireme): iOS鎖的...
    LiLS閱讀 1,625評論 0 6
  • 翻譯:Synchronization 同步 應(yīng)用程序中存在多個線程會導(dǎo)致潛在的問題,這些問題可能會導(dǎo)致從多個執(zhí)行線...
    AlexCorleone閱讀 2,715評論 0 4

友情鏈接更多精彩內(nèi)容