3.2.1 Dispatch Queue
開發者要做的只是定義想執行的任務并追加到適當的 Dispatch Queue 中。
Dispatch Queue 按照追加的順序(先進先出 FIFO,First-In-First-Out)執行處理。
Dispatch Queue 可分為以下2種:
- Serial Dispatch Queue (等待現在執行中處理結束)
- Concurrent Dispatch Queue (不等待現在執行中處理結束)
Concurrent Dispatch Queue 并行執行的處理數受以下因素影響:
- Dispatch Queue 中的處理數
- CPU內核數
- CPU負荷
并行執行,就是使用多個線程同時執行多個處理。如下圖。
如何才都能得到以上提到的兩種 Dispatch Queue,有兩種方法。(往下看)
3.2.2 dispatch_queue_create (方法1)
// 生成 Serial Dispatch Queue
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("這個字符串用來標記這個線程,根據開發需要命名", NULL);
// 生成 Concurrent Dispatch Queue
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("這個字符串用來標記這個線程,根據開發需要命名", DISPATCH_QUEUE_CONCURRENT);
- 多個 Serial Dispatch Queue 可并發執行。
- 系統對于一個 Serial Dispatch Queue 就只能生成并使用一個線程,如果生成2000個 Serial Dispatch Queue,那么就是生成2000個線程。
- 如果過多使用多線程,就會消耗大量內存,引起大量的上下文切換,大幅度降低系統的響應性能。
(todo: 寫一下關于上下文切換的帖子)
生成的 Dispatch Queue 需要程序員負責釋放。
通過 dispatch_queue_create 生成的 Dispatch Queue 由 dispatch_release 函數釋放。
dispatch_release(myConcurrentDispatchQueue); // 非arc的環境下
3.2.3 Main Dispatch Queue / Global Dispatch Queue (方法2)
獲取系統標準提供的 Dispatch Queue。
比方法1更為方便快捷
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 可并行執行的處理
dispatch_async(dispatch_get_main_queue(), ^{
// 只能在主線程中執行的處理
});
});
這里的 DISPATCH_QUEUE_PRIORITY_DEFAULT 宏定義如下,第一個參數通常直接填 0。
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
3.2.4 dispatch_set_target_queue
用來變更Dipatch Queue的優先級。
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("這個字符串用來標記這個線程,根據開發需要命名", NULL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// mySerialDispatchQueue 的優先級變為 DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);
3.2.5 dispatch_after
跟定時器差不多,不準確,有可能等待時間大于3秒,這是受當前 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 last three seconds");
});
第二個參數指定要追加處理的 Dispatch Queue。
第一個參數是 dispatch_time_t 類型。
/* dispatch_time 生成 dispatch_time_t 所需的第一個參數 */
#define DISPATCH_TIME_NOW (0ull)
#define DISPATCH_TIME_FOREVER (~0ull)
3.2.6 Dispatch Group
dispatch_group_notify
在所有追加進去的處理都結束了之后,在主線程執行處理。
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(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
// dispatch_group_notify 第一個參數為要監視的 Dispatch Group,
// 將第三個參數的 Block 追加到 第二個參數的 Dispatch Queue 中。
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
dispatch_release(group);
輸出結果:
blk1
blk2
blk0
done
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(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
從字面上就能理解,dispatch_group_wait 的第二個參數表示等待的時間,DISPATCH_TIME_FOREVER 意味著永久等待。
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == 0) {
// 屬于 Dispatch Group 的全部處理執行結束
} else {
// 還在執行
}
dispatch_group_wait 的第二個參數為等待的時間(超時時間)。
如果在這個時間之后,result == 0,說明追加進去的處理都執行結束了。
如果 dispatch_group_wait 第二個參數的time為 DISPATCH_TIME_NOW,就不用任何等待就可以判定屬于 Dipatch Group 的處理是否執行結束。
3.2.7 dispatch_barrier_async
書本在這一小節里提及最多的就是數據的讀寫,數據競爭等問題。
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.ForBarrier", 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);
3.2.8 dispatch_sync
dispatch_sync 是簡易版的 dispatch_group_wait 函數。
容易因此死鎖,盡量避免使用吧。
3.2.9 dispatch_apply
該函數按指定的次數將指定的 Block 追加到指定的 Dispatch Queue 中,并等待全部處理執行結束。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu", index);
});
NSLog(@"done");
執行結果
4
1
0
3
5
2
6
8
9
7
done
dispatch_apply 會等待全部處理執行結束。
第一個參數為重復次數,第二個參數為追加對象的 Dispatch Queue,第三個參數為追加的處理。
因為 dispath_apply 與 dispatch_sync 相同,會等待處理執行結束,因此推薦在 dispatch_async 函數中非同步地執行 dispatch_apply 函數。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_apply([array count], queue, ^(size_t index) {
// 在這里處理數組里邊的元素
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"done");
});
});
3.2.10 dispatch_suspend / dispatch_resume
dispatch_suspend 函數掛起指定的 Dispatch Queue。
dispatch_resume 函數恢復指定的 Dispatch Queue。
這些函數對已經執行的處理沒有影響。
3.2.11 Dispatch Semaphore
這么理解,通過 dispatch_semaphore_create 創建的 semaphore 有一個計數器(第一個參數),而 dispatch_semaphore_wait 函數在遇到 semphore(第一個參數) 大于等于1的情況下才能繼續往下執行,dispatch_semaphore_signal 函數可以給 semphore 的計數器加1。
通過以上方式可以進行排異處理,仔細體會以下代碼。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 1000; ++i) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject:[NSNumber numberWithInt:i]];
dispatch_semaphore_signal(semaphore);
});
}
3.2.12 dispatch_once
用來創建單例。
static dispatch_once_t pred;
dispatch_once(&pred, ^{
// 初始化
});
3.2.13 Dispatch I/O
同多 Dispatch I/O 讀寫文件時,使用 Global Dispatch Queue 將1個文件按某個大小 read/write。