iOS線程同步,線程和鎖

iOS基礎開發!!!


線程同步:即當有一個線程在對內存進行操做時,其餘線程都不能夠對這個內存地址進行操做,直到該線程完成操做, 其餘線程才能對該內存地址進行操做。面試

因此這裏同步應該不是一塊兒、共同完成的意思,可理解爲協調就是按預約的前後次序進行工做,比如: '不要和我搶了,你先等會我作完了你在作'。算法

線程同步目的爲了多個線程都能很好的工做,合理的訪問系統資源不爭不搶、和諧共處。iOS開發中經常使用的保持線程同步有如下幾種:markdown

  • 經過線程加鎖
  • 串行隊列
  • GCD

線程加鎖

經常使用的幾種形式的鎖網絡

  • 一、 @synchronized
- (void)myMethod:(id)anObj
{
    @synchronized(anObj)
    {
        //執行的代碼操做
    }
}
複製代碼

經過***synchronized指令***自動的添加一個互斥鎖,底層經過pthread_mutex實現。經過對一段代碼的使用進行加鎖。其餘試圖執行該段代碼的線程都會被阻塞,直到加鎖線程退出執行該段被保護的代碼段。多線程

當在@synchronized()代碼塊中拋出異常的時候,Objective-C運行時會捕獲到該異常,並釋放信號量,並把該異常從新拋出給下一個異常處理者。架構

一個線程是能夠以遞歸的方式屢次調用***myMethod***。async

關於參數***anObj***;oop

做爲一個惟一標識符來標記當前線程加鎖操做必須是個對象類型,因此對於同一個操做不一樣的線程應該用同一個對象,不然沒法起到標記加鎖的做用。 不能爲空nil。學習

常見的基本都是***self***url

@synchronized(self)
{
    //執行的代碼操做
}
複製代碼

self做爲標記符十分常見,可是很明顯會有一個問題:

//方法1
- (void)myMethod1:(id)anObj
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0), ^{
        @synchronized(anObj)
        {
            //執行的代碼操做
        }
    });
}
//方法2
- (void)myMethod2:(id)anObj
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0), ^{
        @synchronized(anObj)
        {
            //執行的代碼操做
        }
    });
}

myMethod1(self);
myMethod2(self)</pre>
複製代碼

若是myMethod一、myMethod2沒用任何關係,若是此時執行myMethod1,那麼myMethod2就只能等待其執行完成。因此這種狀況更細的粒度來加鎖,使用各自的對象互不影響更爲合理。


* 二、NSLock ``` NSLock * lock = [[NSLock alloc]init]; [lock lock]; //執行的代碼操做 [lock unlock]; ``` 底層經過pthread_mutex實現;方法lock、unlock必須成對出現,必須在同一個線程中操做不然無效。不支持遞歸,若是屢次調用會形成死鎖。

若是多個線程共用一個**lock ,一個線程加鎖後其餘請求加鎖的線程會造成一個等待隊列、按照先進先出的規則等待鎖釋放後再加鎖(待驗證)


* 三、NSRecursiveLock 遞歸鎖相似NSLock,但它能夠在同一個線程中反覆加鎖且不會形成死鎖。
* 四、 NSCondition `基於信號量方式實現的鎖對象,提供單獨的信號量管理接口。底層經過pthread_cond_t實現。`

NSCondition對象包含鎖和條件檢測功能,相似於生產者和消費者:消費者消費資源若是沒有就繼續等待,生產者提供資源而後發出信號激活消費者。鎖的做用就是用來保護這一操做防止被其餘線程干擾。

DEMO:

__weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _user];
    });
複製代碼
[weakSelf _produce];
    });
複製代碼
-(void)_user{
    [condition lock];

    while (isWait) {
        //等待其餘線程發出信號,[condition signal];
        //阻塞當前線程
        NSLog(@"等待條件知足");
        [condition wait];
    }
    {
        //執行操做
        NSLog(@"執行操做");
    }

    //完成
    [condition unlock];

    NSLog(@"完成");
}

-(void)_produce{
    [condition lock];
     isWait = false;
     [condition signal];
     [condition unlock];
}
複製代碼

輸出結果:

[13781:212898] 等待條件知足
[13781:212898] 執行操做
[13781:212898] 完成</pre>
複製代碼

* ####五、 NSConditionLock 可使用特定值來加鎖和解鎖,和**`NSCondition`**表現差很少。
  • (instancetype)initWithCondition:(NSInteger)condition 參數**condition做爲標識符更容易理解,lockWhenCondition獲取指定標記的鎖沒有的話就阻塞當前線程,unlockWithCondition:**釋放指定標記的鎖,等他的線程獲取鎖而後繼續執行操做。

使用上比**NSCondition**更方便些,代碼更簡潔。

用**NSConditionLock**改寫以上代碼:

-(void)_testConditionLock{
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _user1];
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _produce1];
    });
}
-(void)_user1{
    NSLog(@"等待條件知足");
    [conditionLock lockWhenCondition:11];

    NSLog(@"條件知足了");

    {
        //執行操做
        NSLog(@"執行操做");
    }

    //完成
    [conditionLock unlockWithCondition:0];

    NSLog(@"完成");
}

[self _testConditionLock];</pre>
複製代碼

輸出結果:

[7812:120141] 等待條件知足
[7812:120137] 生成條件中...
[7812:120141] 條件知足了
[7812:120141] 執行操做
[7812:120141] 完成</pre>
複製代碼

* #### 六、 其餘不經常使用的鎖 pthread_mutex pthread_mutex(recursive) POSIX標準的unix多線程,C 語言下多線程實現。

***OSSpinLock:***自旋鎖,一直輪詢等待時會消耗大量 CPU 資源。

串行隊列

經過建立一個串行隊列,把咱們的操做添加到隊列。

dispatch_queue_t queue = dispatch_queue_create("com.queue.test",DISPATCH_QUEUE_SERIAL);

dispatch_async(queue, ^{
    NSLog(@"task 1");
});
dispatch_async(queue, ^{
    NSLog(@"task 2");
});

dispatch_async(queue, ^{
    NSLog(@"task 3");
});</pre>
複製代碼

感受建立隊列、添加操做到隊列太麻煩,不夠簡潔並且隊列的調度確定佔用很多資源.


### GCD

經過dispatch_semaphore信號量實現線程同步

dispatch_semaphore_create(long value);

dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);//-1

dispatch_semaphore_signal(dispatch_semaphore_t dsema);//+1</pre>
複製代碼

***dispatch_semaphore_wait在信號量爲0時會阻塞當前線程,等待dispatch_semaphore_signal***釋放信號而後繼續執行。

用信號量改寫以上代碼:

-(void)_testSemaphore{
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _user2];
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _produce2];
    });
}
-(void)_user2{
    NSLog(@"等待條件知足");
    dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);

    NSLog(@"條件知足了");

    {
        //執行操做
        NSLog(@"執行操做");
    }

    //完成
    NSLog(@"完成");
}

-(void)_produce2{
    NSLog(@"生成條件中...");
     dispatch_semaphore_signal(semaphore);
}
複製代碼
semaphore = dispatch_semaphore_create(0);
    NSLog(@"初始化信號0");

    [self _testSemaphore];</pre>
複製代碼

輸出:

[9581:159120] 初始化信號0

[9581:159194] 生成條件中... [9581:159195] 等待條件知足 [9581:159195] 條件知足了 [9581:159195] 執行操做 [9581:159195] 完成


總結

經常使用的線程間同步方式就這些了,實際中用的信號量和NSLock比較多。至於其餘的不是由於很差而是由於習慣了,不到很必須的時候我感受都差很少。真正由於其自己所佔用的開銷通常可忽略不計。

實踐前先後後持續了一週的時間,總算進一步加深了認知。寫完了才感受這些知識才是本身的,而後在慢慢吸取、消化,才能伴隨咱們一步步的走向強大。

將來的路很長,不知道會走多遠,只想走好腳下的每一步!

做爲一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是個人iOS交流圈: 無論你是小白仍是大牛歡迎入駐!! 分享內容包括跳槽面試寶典、逆向安防、算法、架構設計、多線程,網絡進階,還有底層、音視頻、Flutter等等......

本身根據梳理網絡來的的開發經驗總結的學習方法,無償分享給你們。更多資源,須要的話均可以自行來獲取下載。 +裙:196800191、 或者是+ WX(XiAZHiGardenia)免費獲取! 獲取面試資料 簡歷模板 一塊兒交流技術資源集合


謝謝你長的這麼好看,還關注我!!!點個讚唄!!
若是有什麼不對的請各位大佬留言提示,而後有什麼別的須要改進的提示請聯繫我我會及時補充~

over.over.

部分文章來自轉載網絡,若有侵權請聯繫做者刪除

相關文章
相關標籤/搜索