iOS開發-多線程開發之線程安全篇

前言:一塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源,好比多個線程訪問同一個對象、同一個變量、同一個文件和同一個方法等。所以當多個線程訪問同一塊資源時,很容易會發生數據錯誤及數據不安全等問題。所以要避免這些問題,咱們須要使用「線程鎖」來實現。html

 

本文主要論述IOS建立鎖的方法總結,若是你們對多線程編程技術這一塊不熟悉,我建議大家先去看個人另外一篇文章」iOS開發-多線程編程技術(Thread、Cocoa operations、GCD)編程

 

1、使用關鍵字安全

1)@synchronized(互斥鎖)多線程

優勢:使用@synchronized關鍵字能夠很方便地建立鎖對象,並且不用顯式的建立鎖對象。async

缺點:會隱式添加一個異常處理來保護代碼,該異常處理會在異常拋出的時候自動釋放互斥鎖。而這種隱式的異常處理會帶來系統的額外開銷,爲優化資源,你可使用鎖對象。分佈式

2、Object-C」語言學習

1)NSLock(互斥鎖)優化

2)NSRecursiveLock(遞歸鎖)ui

條件鎖,遞歸或循環方法時使用此方法實現鎖,可避免死鎖等問題。spa

3)NSConditionLock(條件鎖)

使用此方法能夠指定,只有知足條件的時候才能夠解鎖。

4)NSDistributedLock(分佈式鎖)

在IOS中不須要用到,也沒有這個方法,所以本文不做介紹,這裏寫出來只是想讓你們知道有這個鎖存在。

若是想要學習NSDistributedLock的話,你能夠建立MAC OS的項目本身演練,方法請自行Google,謝謝。

3、C語言

1)pthread_mutex_t(互斥鎖)

2)GCD-信號量(「互斥鎖」)

3)pthread_cond_t(條件鎖)

 

線程安全 —— 鎖

1、使用關鍵字

1)@synchronized

// 實例類person
Person *person = [[Person alloc] init];

// 線程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(person) { [person personA]; [NSThread sleepForTimeInterval:3]; // 線程休眠3秒  } }); // 線程B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(person) { [person personB]; } });

關鍵字@synchronized的使用,鎖定的對象爲鎖的惟一標識,只有標識相同時,才知足互斥。若是線程B鎖對象person改成self或其它標識,那麼線程B將不會被阻塞。你是否看到@synchronized(self) ,也是對的。它能夠鎖任何對象,描述爲@synchronized(anObj)。

 

2、Object-C語言

1)使用NSLock實現鎖

// 實例類person
Person *person = [[Person alloc] init];
// 建立鎖
NSLock *myLock = [[NSLock alloc] init];

// 線程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [myLock lock];
    [person personA];
    [NSThread sleepForTimeInterval:5];
    [myLock unlock];
});

// 線程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [myLock lock];
    [person personB];
    [myLock unlock];
}); 

程序運行結果:線程B會等待線程A解鎖後,纔會去執行線程B。若是線程Blockunlock方法去掉以後,則線程B不會被阻塞,這個和synchronized的同樣,須要使用一樣的鎖對象纔會互斥。

NSLock類還提供tryLock方法,意思是嘗試鎖定,當鎖定失敗時,不會阻塞進程,而是會返回NO。你也可使用lockBeforeDate:方法,意思是在指定時間以前嘗試鎖定,若是在指定時間前都不能鎖定,也是會返回NO

注意:鎖定(lock)和解鎖(unLock)必須配對使用

 

2)使用NSRecursiveLock類實現鎖 

// 實例類person
Person *person = [[Person alloc] init];
// 建立鎖對象
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];

// 建立遞歸方法
static void (^testCode)(int);
testCode = ^(int value) {
    [theLock tryLock];
    if (value > 0)
    {
        [person personA];
        [NSThread sleepForTimeInterval:1];
        testCode(value - 1);
    }
    [theLock unlock];
};

//線程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    testCode(5);
});

//線程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [theLock lock];
    [person personB];
    [theLock unlock];
});

若是咱們把NSRecursiveLock類換成NSLock類,那麼程序就會死鎖。由於在此例子中,遞歸方法會形成鎖被屢次鎖定(Lock),因此本身也被阻塞了。而使用NSRecursiveLock類,則能夠避免這個問題。

 

3)使用NSConditionLock(條件鎖)類實現鎖:

使用此方法能夠建立一個指定開鎖的條件,只有知足條件,才能開鎖。

// 實例類person
Person *person = [[Person alloc] init];
// 建立條件鎖
NSConditionLock *conditionLock = [[NSConditionLock alloc] init];

// 線程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lock];
    [person personA];
    [NSThread sleepForTimeInterval:5];
    [conditionLock unlockWithCondition:10];
});

// 線程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lockWhenCondition:10];
    [person personB];
    [conditionLock unlock];
});

線程A使用的是lock方法,所以會直接進行鎖定,而且指定了只有知足10的狀況下,才能成功解鎖。

unlockWithCondition:方法,建立條件鎖,參數傳入「整型」。lockWhenCondition:方法,則爲解鎖,也是傳入一個「整型」的參數。

 

3、C語言

1)使用pthread_mutex_t實現鎖

注意:必須在頭文件導入:#import <pthread.h>

// 實例類person
Person *person = [[Person alloc] init];

// 建立鎖對象
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

// 線程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personA];
    [NSThread sleepForTimeInterval:5];
    pthread_mutex_unlock(&mutex);
});

// 線程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personB];
    pthread_mutex_unlock(&mutex);
});

實現效果和上例的相一致

  

2)使用GCD實現(信號量)

GCD提供一種信號的機制,使用它咱們能夠建立「鎖」(信號量和鎖是有區別的,具體請自行百度)。

// 實例類person
Person *person = [[Person alloc] init];

// 建立並設置信量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

// 線程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [person personA];
    [NSThread sleepForTimeInterval:5];
    dispatch_semaphore_signal(semaphore);
});

// 線程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [person personB];
    dispatch_semaphore_signal(semaphore);
});

效果也是和上例介紹的相一致。

我在這裏解釋一下代碼。dispatch_semaphore_wait方法是把信號量加1,dispatch_semaphore_signal是把信號量減1。

咱們把信號量看成是一個計數器,當計數器是一個非負整數時,全部經過它的線程都應該把這個整數減1。若是計數器大於0,那麼則容許訪問,並把計數器減1。若是爲0,則訪問被禁止,全部經過它的線程都處於等待的狀態。

 

3)使用POSIX(條件鎖)建立鎖

// 實例類person
Person *person = [[Person alloc] init];

// 建立互斥鎖
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
// 建立條件鎖
__block pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

// 線程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    pthread_cond_wait(&cond, &mutex);
    [person personA];
    pthread_mutex_unlock(&mutex);
});

// 線程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personB];
    [NSThread sleepForTimeInterval:5];
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
});

效果:程序會首先調用線程B,在5秒後再調用線程A。由於在線程A中建立了等待條件鎖,線程B有激活鎖,只有當線程B執行完後會激活線程A。

pthread_cond_wait方法爲等待條件鎖。

pthread_cond_signal方法爲激動一個相同條件的條件鎖。

 

 

簡單總結:

通常來講,若是項目不大,咱們都會偷點懶,直接使用關鍵字@synchronized創建鎖,懶人方法。其次可使用蘋果提供的OC方法,最後纔會去使用C去創建鎖。

 

 

 

 

本文參考文章:

iOS多線程開發(四)---線程同步

Objective-C中不一樣方式實現鎖(一)

信號量與互斥鎖

 

 

 


博文做者:GarveyCalvin

博文出處:http://www.cnblogs.com/GarveyCalvin/

本文版權歸做者和博客園共有,歡迎轉載,但須保留此段聲明,並給出原文連接,謝謝合做!

相關文章
相關標籤/搜索