1. 程式人生 > >多執行緒,訊號量的簡單使用 GCD

多執行緒,訊號量的簡單使用 GCD

基本概念

關於iOS開發中,多執行緒基本的概念和基本使用,我在這裡就不在重複說了。但是為了照顧到有的同學可能還不是對基本的概念不熟悉,可以參考一下這篇文章併發其實很簡單

說說訊號量,併發數

如果你有計算機基礎,那麼下面這段話應該很簡單就能理解

訊號量就是一個資源計數器,對訊號量有兩個操作來達到互斥,分別是P和V操作。 一般情況是這樣進行臨界訪問或互斥訪問的: 設訊號量值為1, 當一個程序1執行是,使用資源,進行P操作,即對訊號量值減1,也就是資源數少了1個。這是訊號量值為0。系統中規定當訊號量值為0是,必須等待,知道訊號量值不為零才能繼續操作。 這時如果程序2想要執行,那麼也必須進行P操作,但是此時訊號量為0,所以無法減1,即不能P操作,也就阻塞。這樣就到到了程序1排他訪問。 當程序1執行結束後,釋放資源,進行V操作。資源數重新加1,這是訊號量的值變為1. 這時程序2發現資源數不為0,訊號量能進行P操作了,立即執行P操作。訊號量值又變為0.次數程序2咱有資源,排他訪問資源。 這就是訊號量來控制互斥的原理
簡單來講 訊號量為0則阻塞執行緒,大於0則不會阻塞。則我們通過改變訊號量的值,來控制是否阻塞執行緒,從而達到執行緒同步。

當然再NSoperation下可以直接設定併發數,就沒有這麼麻煩了。

GCD如何使用訊號量

我們使用GCD的時候如何讓執行緒同步,目前我能想到的就三種

1.dispatch_group
2.dispatch_barrier
3.dispatch_semaphore
1和2比較簡單,也是比較常用的。這裡不在介紹。如果不清楚可以參考併發其實很簡單
這裡主要講講dispatch_semaphore

在GCD中有三個函式是semaphore的操作,
分別是:  

dispatch_semaphore_create 建立一個semaphore 
dispatch_semaphore_signal 傳送一個訊號 
dispatch_semaphore_wait 等待訊號
簡單的介紹一下這三個函式,第一個函式有一個整形的引數,我們可以理解為訊號的總量,dispatch_semaphore_signal是傳送一個訊號,自然會讓訊號總量加1,dispatch_semaphore_wait等待訊號,當訊號總量少於0的時候就會一直等待,否則就可以正常的執行,並讓訊號總量-1,根據這樣的原理,我們便可以快速的建立一個併發控制來同步任務和有限資源訪問控制。

看程式碼

// 建立佇列組
    dispatch_group_t group = dispatch_group_create();   
// 建立訊號量,並且設定值為10
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);   
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   
    for (int i = 0; i < 100; i++)   
    {   // 由於是非同步執行的,所以每次迴圈Block裡面的dispatch_semaphore_signal根本還沒有執行就會執行dispatch_semaphore_wait,從而semaphore-1.當迴圈10此後,semaphore等於0,則會阻塞執行緒,直到執行了Block的dispatch_semaphore_signal 才會繼續執行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_group_async(group, queue, ^{ NSLog(@"%i",i); sleep(2); // 每次傳送訊號則semaphore會+1, dispatch_semaphore_signal(semaphore); }); }

上面的註釋已經把如何通過控制訊號量來控制執行緒同步解釋的比較淺顯了。關鍵就是這句:

//
由於是非同步執行的,所以每次迴圈Block裡面的dispatch_semaphore_signal根本還沒有執行就會執行dispatch_semaphore_wait,從而semaphore-1.當迴圈10此後,semaphore等於0,則會阻塞執行緒,直到執行了Block的dispatch_semaphore_signal
才會繼續執行

實際應用

在開發中我們需要等待某個網路回撥完之後才執行後面的操作,根據啥給你們分析的過程,可以寫出如下程式碼,達到這種效果。

    _block BOOL isok = NO;  

    dispatch_semaphore_t sema = dispatch_semaphore_create(0);  
    Engine *engine = [[Engine alloc] init];  
    [engine queryCompletion:^(BOOL isOpen) {  
        isok = isOpen;  
        dispatch_semaphore_signal(sema);  
    } onError:^(int errorCode, NSString *errorMessage) {  
        isok = NO;  
        dispatch_semaphore_signal(sema);  
    }];  

    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);  
    // todo what you want to do after net callback

在具體一點,做通訊錄的時候需要判斷許可權,才能獲取通訊錄。如果我沒記錯。再iOS9之前可以通過如下方式獲得。這也是我第一次實際應用訊號量的時候。
獲取通訊錄

//建立通訊簿的引用        
addBook=ABAddressBookCreateWithOptions(NULL, NULL);        //建立一個出事訊號量為0的訊號       
 dispatch_semaphore_t sema=dispatch_semaphore_create(0);        //申請訪問許可權       
 ABAddressBookRequestAccessWithCompletion(addBook, ^(bool greanted, CFErrorRef error)      
    {
            //greanted為YES是表示使用者允許,否則為不允許           
 if (!greanted) {   
            tip=1;         
   }            //傳送一次訊號            dispatch_semaphore_signal(sema);  

     });        //等待訊號觸發     
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

原文出處:http://www.jianshu.com/p/04ca5470f212