GCD基本使用

GCD兩個基本知識:任務和隊列;

任務執行方式分爲兩種:同步(sync)和異步(async)
隊列也分爲兩種:併發隊列和串行隊列;
各類組合方式:
異步函數+併發隊列:開啓多條線程,併發執行任務
異步函數+串行隊列:開啓一條線程,串行執行任務
同步函數+併發隊列:不開線程,串行執行任務
同步函數+串行隊列:不開線程,串行執行任務
異步函數+主隊列:不開線程,在主線程中串行執行任務
同步函數+主隊列:不開線程,串行執行任務(發生死鎖)

GCD基本函數:

 

dispatch_barrier_async/dispatch_group_notify/dispatch_semaphore_wait 不會阻塞線程
dispatch_group_wait 會阻塞線程

 

  • dispatch_barrier_async 控制任務的執行順序

  1. queue必須是手動建立的並行隊列;
  2. 全部位於dispatch_barrier_async函數以前的操做執行完畢後dispatch_barrier_async纔會執行;
  3. dispatch_barrier_async函數執行以後,barrier函數以後的操做纔會獲得執行;
  4. 該函數須要同dispatch_queue_create函數生成的concurrent Dispatch Queue隊列一塊兒使用;
dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for(int i = 0; i < 5; i++){
            NSLog(@"11111111---%@", [NSThread currentThread]);
        }
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"--dispatch_barrier_async-");
    });
    
    dispatch_async(queue, ^{
        for(int i = 0; i < 5; i++){
            NSLog(@"222222---%@", [NSThread currentThread]);
        }
    });

  執行結果:
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634366] --dispatch_barrier_async-
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}

 dispatch_barrier_async並不會阻塞線程,同一個併發隊列中的操做等待dispatch_barrier_async執行完以後纔會執行,其餘操做不會等dispatch_barrier_async執行完以後再執行;多線程

dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"task1");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task2");
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier_async");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task3");
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"task4");
    });
    
    NSLog(@"---end");

執行結果:
2017-10-12 10:16:29.368495+0800 test[5491:2328009] ---end
2017-10-12 10:16:29.370267+0800 test[5491:2328077] task1
2017-10-12 10:16:29.370539+0800 test[5491:2328733] task2
2017-10-12 10:16:29.370596+0800 test[5491:2328077] task4
2017-10-12 10:16:29.370677+0800 test[5491:2328733] barrier_async
2017-10-12 10:16:29.370795+0800 test[5491:2328733] task3

能夠看到task4和輸出end 不會等barrier_async執行完再執行;
  • dispatch_after

dispatch_after函數不是延遲N秒再執行block,而是在N秒以後纔將block加入到隊列中去; 併發

延遲時間有兩種計算方式:
一、相對時間:
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC) 表示在當前時間2秒後
DISPATCH_TIME_NOW:當前時間
NSEC_PER_SEC 表示秒數
NSEC_PER_MSEC表示毫秒


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"---%@",[NSThread currentThread]);
    });
    
二、絕對時間:絕對時間須要用 dispatch_walltime 函數建立;

dispatch_walltime(const struct timespec *_Nullable when, int64_t delta);
第一個參數是timespec結構體,
_STRUCT_TIMESPEC
{
	__darwin_time_t	tv_sec; //秒數的整數部分
	long            tv_nsec; //秒數的小數部分
};


示例代碼:
    struct timespec spec = {0, 0};

    //獲取當前時間作測試
    NSDate *date = [NSDate date];
    //計算當前時間到1970的秒數
    NSTimeInterval timeInterval = [date timeIntervalSince1970];
    
    timeInterval += 5;  //在當前時間延遲5秒執行
    
    double second = 0;  //秒數的整數部分
    double subSecond = 0;   //秒數的小數部分
    
    //modf函數的做用是求出整數部分和小數部分
    subSecond = modf(timeInterval, &second);
    
    spec.tv_sec = second;
    spec.tv_nsec = subSecond * NSEC_PER_SEC;
    
    dispatch_time_t time2= dispatch_walltime(&spec, 0);
    
    dispatch_after(time2, dispatch_get_global_queue(0, 0), ^{
       
        
        NSLog(@"111111");
    });
  •  dispatch_once

dispatch_once函數一般用在單例模式上,它能夠保證在程序運行期間某段代碼只執行一次 app

-(void)once
    {
        //整個程序運行過程當中只會執行一次
        //onceToken用來記錄該部分的代碼是否被執行過
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{

            NSLog(@"-----");
        });
    }
  •  dispatch_apply

  1. 把指定的block添加到queue中N次,等到全部block都執行完以後才返回;
  2.  第一個參數是迭代次數,第二個是所在的隊列,第三個是當前索引;
  3. dispatch_apply能夠利用多核的優點;
  4. dispatch_apply 和 dispatch_apply_f 是同步函數,會阻塞當前線程直到全部循環迭代執行完成;
  5. 當提交到併發queue時,循環迭代的執行順序是不肯定的 ;
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
        
        NSLog(@"%zd", index);
    });

輸出結果:
2017-04-27 11:13:59.439 newTest[3563:1745064] 1
2017-04-27 11:13:59.439 newTest[3563:1745019] 0
2017-04-27 11:13:59.440 newTest[3563:1745019] 3
2017-04-27 11:13:59.440 newTest[3563:1745064] 2
2017-04-27 11:13:59.441 newTest[3563:1745064] 5
2017-04-27 11:13:59.441 newTest[3563:1745019] 4
2017-04-27 11:13:59.441 newTest[3563:1745064] 6
2017-04-27 11:13:59.441 newTest[3563:1745064] 8
2017-04-27 11:13:59.441 newTest[3563:1745019] 7
2017-04-27 11:13:59.441 newTest[3563:1745064] 9
  •  dispatch_group 隊列組(同柵欄函數)

下面有兩種方式來實現 等待某些操做完成後纔開始執行後續操做,
    方法1:

     dispatch_group_notify:當 dispatch_group中全部的block都執行完後,
     dispatch_group_notify會通知隊列任務都執行完畢,並執行 dispatch_group_notify 的block;
     dispatch_group_notify不會阻塞隊列;
     dispatch_group_wait : 同步的等待隊列中的任務都執行完畢,纔會執行後續的任務;會阻塞隊列;

    dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"%zd-download1--%@",i,[NSThread currentThread]);
        }
        
    });
    
    dispatch_group_async(group, queue, ^{
        
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"%zd-download2--%@",i,[NSThread currentThread]);
        }
        
    });
    
    dispatch_group_notify(group, queue, ^{
       
        NSLog(@"dispatch_group_notify");
        
    });
 
    方法2:dispatch_group_enter、dispatch_group_wait
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
        
    NSLog(@"group enter");
        
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for(int i = 0; i < 10; i++){
            NSLog(@"%zd----%@", i, [NSThread currentThread]);
        }
        
        NSLog(@"group leave");
        dispatch_group_leave(group);
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
         NSLog(@"another task");
        
    });
須要注意的是,dispatch_group_wait實際上會使當前的線程處於等待的狀態,
也就是說若是是在主線程執行dispatch_group_wait,在上面的Block執行完以前,主線程會處於卡死的狀態。
能夠注意到dispatch_group_wait的第二個參數是指定超時的時間,
若是指定爲DISPATCH_TIME_FOREVER(如上面這個例子)則表示會永久等待,直到上面的Block所有執行完,
除此以外,還能夠指定爲具體的等待時間,根據dispatch_group_wait的返回值來判斷是上面block執行完了仍是等待超時了。
最後,同以前建立dispatch_queue同樣,若是是在OS X 10.8或iOS 6以及以後版本中使用,
Dispatch Group將會由ARC自動管理,若是是在此以前的版本,須要本身手動釋放。
  • Dispatch Semaphore

信號量在多線程開發中被普遍使用,當一個線程在進入一段關鍵代碼以前,線程必須獲取一個信號量,一旦該關鍵代碼段完成了,那麼該線程必須釋放信號量。其它想進入該關鍵代碼段的線程必須等待前面的線程釋放信號量。
信號量的具體作法是:當信號計數大於0時,每條進來的線程使計數減1,直到變爲0,變爲0後其餘的線程將進不來,處於等待狀態;執行完任務的線程釋放信號,使計數加1,如此循環下去。異步

下面這個例子中使用了10條線程,可是同時只執行一條,其餘的線程處於等待狀態:

//dispatch_semaphore_wait :減小信號量計數.若是結果小於0, 等待信號喚醒.
//dispatch_semaphore_signal : 增長信號量計數,若是以前的值小於0 喚醒 dispatch_semaphore_wait
    
    //第一步:初始信號量= 0
    semaphore = dispatch_semaphore_create(0);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        for(NSInteger i = 0; i < 10; i++){
            NSLog(@"--------------%zd", i);
        }
        
        //第三步:任務執行完 判斷信號量小於0  喚醒dispatch_semaphore_wait
        dispatch_semaphore_signal(semaphore);
    });
    
    //第二步:信號量減1 變成 -1,小於0 等待喚醒
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    NSLog(@"dddddddddddddddddd");
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        
    });


取得信號量的線程在2秒後釋放了信息量,至關因而每2秒執行一次。
經過上面的例子能夠看到,在GCD中,用dispatch_semaphore_create函數能初始化一個信號量,同時須要指定信號量的初始值;
使用dispatch_semaphore_wait函數分配信號量並使計數減1,爲0時處於等待狀態;
使用dispatch_semaphore_signal函數釋放信號量,並使計數加1。
另外dispatch_semaphore_wait一樣也支持超時,只須要給其第二個參數指定超時的時候便可,
同Dispatch Group的dispatch_group_wait函數相似,能夠經過返回值來判斷。
這個函數也須要注意,若是是在OS X 10.8或iOS 6以及以後版本中使用,Dispatch Semaphore將會由ARC自動管理,
若是是在此以前的版本,須要本身手動釋放。
相關文章
相關標籤/搜索