iOS-多線程與鎖

前言ios

項目中使用多線程,首要考慮的問題就是線程安全問題,廣泛存在的是多個線程同時訪問同一內存(存在讀寫操做,單獨的讀操做是不會發生衝突的)發生的數據衝突或者是crash。每每咱們的解決方案通常採用同步隊列或者是加鎖,可是要考慮死鎖問題。swift

下面解釋幾個名詞;安全

(1)隊列類型網絡

同步隊列:一個一個按照FIFO的執行任務,後一個任務須要等待前一個任務執行完成後才能被執行;多線程

併發隊列:後一個任務不須要等待前一個任務執行完成就能夠被執行,如果異步提交任務到異步隊列則能夠併發執行多個任務,如果同步提交任務則是一個一個執行任務。併發

(2)執行方式異步

同步方式會阻塞當前線程,異步方式不會阻塞當前線程(當即返回)。ui

(3)加鎖spa

對於鎖的解釋網絡上有不少文章,看下面幾篇文章線程

深刻理解 iOS 開發中的鎖

【iOS】從同步鎖到多線程

非遞歸鎖,如NSLock等;

遞歸鎖:同一個線程能夠屢次加鎖而不會形成死鎖,但會阻塞其餘線程訪問(lock與unlock成對出現)。官方文檔對遞歸鎖NSRecursiveLock的解釋;

A recursive lock is a variant on the mutex lock. A recursive lock allows a single thread to acquire the lock multiple times before releasing it. Other threads remain blocked until the owner of the lock releases the lock the same number of times it acquired it. Recursive locks are used during recursive iterations primarily but may also be used in cases where multiple methods each need to acquire the lock separately.

 

容易出現死鎖的狀況;

(1)使用隊列形成的死鎖,同一線程屢次同步提交任務到同步隊列,同步執行方式會阻塞當前線程,而同步隊列想要執行後一個任務必需要等到前一個任務完成後,後一個任務纔會被執行,形成先後任務相互等待線程休眠,最後致使死鎖。比較經典的死鎖的例子;

dispatch_sync(dispatch_get_main_queue(), ^{
        
        NSLog(@"任務二");
    });
    
    NSLog(@"任務一");

因爲同步執行方式阻塞當前線程,任務一等待任務二執行完纔可以執行,可是因爲任務二被添加到任務一以後,因此任務二需等待任務一執行完才能被執行,因而形成了死鎖。

 

(2)使用加鎖形成的死鎖,使用非遞歸鎖在沒有被解鎖的狀況下屢次加鎖則會形成加鎖死鎖,緣由是未解鎖屢次加鎖會出現前一次加鎖想沒法解鎖,後一次加鎖獲取不到鎖對象的狀況,像隊列同樣,會形成相互等待線程休眠的狀況,從而致使死鎖。看一個例子;

NSLock *theLock = [[NSLock alloc] init];
//NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
 
void MyRecursiveFunction(int value)
{
    [theLock lock];
    if (value != 0)
    {
        --value;
        MyRecursiveFunction(value);
    }
    [theLock unlock];
}
 
MyRecursiveFunction(5);

上述代碼是在遞歸調用中使用非遞歸鎖加鎖,會形成加鎖死鎖,改爲遞歸鎖後正常執行。

 

總結

使用多線程不只要考慮線程安全,還須要考慮死鎖的狀況,今天只是分析如下兩種致使死鎖的狀況;

(1)同步提交任務至同步隊列;

(2)在有遞歸或者是循環狀況下,使用非遞歸鎖加鎖。

相關文章
相關標籤/搜索