天天看點

Grand Central Dispatch (GCD) 用法詳細介紹

1. Dispatch Queue 執行處理有兩種Dispatch Queue。 一種是等待現在執行中的處理的Serial Dispatch Queue。(順序執行) 另一種是不等待現在執行中處理的Concurrent Dispatch Queue。(并行執行)

Concurrent Dispatch Queue執行:

線程0 線程1 線程2 線程3
blk0 blk1 blk2 blk3
blk5 blk6 blk4
blk7

2. dispatch_queue_create 通過 dispatch_queue_create 函數可以生成 Dispathch Queue。

// 第一個參數為queue署名。第二個參數為建立的queue類型。null預設為Serial dispatch queue
dispatch_queue_t myDispatchQueue = dispatch_queue_create(“com.example.queue”, NULL);

// dispatch_queue_t myDispatchQueue = dispatch_queue_create(“com.example.queue”, DIDPATCH_QUEUE_CONCURRENT);

dispatch_async(myDispatchQueue, ^{ NSLog(@“ block on queue “); });

// 結束後需要release
dispatch_release(myDispatchQueue);

// 相應的有retain函數
// dispatch_retain(myDispatchQueue);
           

雖然一執行完 dispatch_async 後裡面釋放queue,但是由于Block持有該Dispatch Queue,是以此處release後 queue 不會被廢棄。

3. Main Dispatch Queue 和 Global Dispatch Queue 以上兩種為系統标準提供的Dispatch Queue;

名稱 Dispatch Queue種類 說明
Main Dispatch Queue Serial Dispatch Queue 主線程執行
DISPATCH_QUEUE_PRIORITY_HIGH Concurrent Dispatch Queue 高優先級
DISPATCH_QUEUE_PRIORITY_DEFAULT Concurrent Dispatch Queue 預設優先級
DISPATCH_QUEUE_PRIORITY_LOW Concurrent Dispatch Queue 低優先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND Concurrent Dispatch Queue 背景執行
// 擷取主線程
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

// 
dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

// 
dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 
dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

// 
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
           

注:dispatch_retain() 和 dispatch_release() 對于這兩種queue不會引起任何變化。

4. dispatch_set_target_queue dispatch_queue_create() 函數生成的無論是Serial 還是 Concurrent ,都是預設優先級。 使用 dispatch_set_target_queue 可以變更優先級。 例如:

dispatch_queue_t myDispatchQueue = dispatch_queue_create(“com.example.queue”, NULL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(myDispatchQueue, globalDispatchQueueBackground);
           

以上代碼将 myDispatchQueue 的優先級變更為 背景優先級。

5. dispatch_after 想要在指定時間後執行處理,可使用 dispatch_after。 例如:3秒後将指定的Block 追加奧 Main Dispatch Queue 中。

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@“waited at least three seconds.”); });
           

注: dispatch_after 函數并不是在指定時間後執行處理,而是在指定時間後追加處理到Dispatch Queue。例如Main Dispatch Queue在主線程的RunLoop中執行。是以在比如每隔1/60秒執行的RunLoop,Block最快在3秒後執行,最慢在 3+1/60秒後執行。

6. Dispatch Group 如果想要在追加到多個Dispatch Queue中的多個處理全部結束後執行結束處理,,可使用Dispatch Group。 例如:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{NSLog(@“bll0”);});
dispatch_group_async(group, queue, ^{NSLog(@“bll1”);});
dispatch_group_async(group, queue, ^{NSLog(@“bll2”);});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@“done”); });
dispatch_release(group);
           

其中可以使用 dispatch_group_wait函數僅等待全部處理執行結束:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{NSLog(@“bll0”);});
dispatch_group_async(group, queue, ^{NSLog(@“bll1”);});
dispatch_group_async(group, queue, ^{NSLog(@“bll2”);});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group); 
           

注:dispatch_group_wait 的傳回值為0,辨別全部處理執行結束; 傳回值不為0,表示經過了指定時間,屬于Dispatch Group 的某一個處理還在執行中; 由于 DISPATCH_TIME_FOREVER 辨別永久等待,故而傳回值恒為0;

dispatch_group_wait 可能會造成目前線程停止,直至所有處理執行結束。

7. dispatch_barrier_async 在多個并行處理之間插入指定處理後再繼續多個并行處理。 例如:

dispatch_queue_t queue = dispatch_queue_create(“com.example.queue”, DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, blk0_for_reading); 
dispatch_async(queue, blk1_for_reading); 
dispatch_async(queue, blk2_for_reading); 
dispatch_async(queue, blk3_for_reading); 
dispatch_barrier_async(queue, blk_for_writing);
dispatch_async(queue, blk4_for_reading); 
dispatch_async(queue, blk5_for_reading); 
dispatch_async(queue, blk6_for_reading); 
dispatch_async(queue, blk7_for_reading); 
dispatch_async(queue, blk8_for_reading); 

dispatch_release(queue);
           

使用 Concurrent Dispatch Queue 和 dispatch_barrier_async 可實作高效率的資料庫通路和檔案通路。

8. dispatch_apply 該函數按指定的次數将指定的Block追加到指定的Dispatch Queue中,并等待全部處理執行結束。 例如:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(5, queue, ^(size_t index){ NSLog(@“%zu”, index); });
NSLog(@“done”);
           
// 運作結果
4
1
0
3
2
done
           

9. dispatch_suspend / dispatch_resume 當追加大量處理到Dispatch Queue時,在追加處理的過程中,希望不執行一追加的處理,例如驗算結果被Block截獲時,一些處理會對這個演算結果造成影響。 可使用以上兩個函數挂起和恢複queue。

10. Dispatch Semaphore 該函數是比 dispatch_barrier_async() 更細粒度的排他控制。 先看一下例子:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray *array = [[NSMutableArray alloc] init];
for(int i = 0; i< 100000; ++i)
{
     dispatch_async(queue, ^{ [array addObject:[NSNumber numberWithInt:i]]; });
}
           

以上代碼執行後由記憶體錯誤導緻應用異常結束的機率很高。 Dispatch Semaphore 是持有計數的信号。當計數為0時,表示等待;計數為1或者大于1時,減去1而不等待;

// 生成函數,計數初始值為1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

// 一直等待,直到Dispatch Semaphore 的計數值達到大于等于1;
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (result == 0)
{
     // 可執行其他需要進行排他控制的處理
}else{
     // 在達到指定時間為止待機
}
           

修改後的例子:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 保證可通路NSMutableArray 的線程同時隻能有一個
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

NSMutableArray *array = [[NSMutableArray alloc] init];
for(int i = 0; i< 100000; ++i)
{
     dispatch_async(queue, ^{ 
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          [array addObject:[NSNumber numberWithInt:i]]; 
     });
} 
dispatch_release(semaphore);
           

11. dispatch_once dispatch_once 函數保證在應用程式執行中隻執行一次指定處理的API; 例如:

static int initialized = NO;
if (initialized == NO)
{
     //initialized
     initialized = YES;
}
           

可轉變成:

static dispatch_once_t pred;
dispatch_once(&pred, ^{
     //initialized
});