Grand Central Dispatch(GCD)是異步執行任務的技術之一。通常將應用程序中記述的線程管理用的代碼在系統級中實現。開發者只須要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。因爲線程管理是做爲系統的一部分來實現的,所以可統一管理,也可執行任務,這樣就比之前的線程更有效率。ios
將想執行的任務追加到適當的Dispatch Queueapi
Dispatch Queue種類 | 說明 |
---|---|
Serial Dispatch Queue | 串行隊列,任務按照追加順序處理(FIFO) |
Concurrent Dispatch Queue | 並行隊列 |
串行隊列只有一個線程,並行隊列有多個線程。bash
dispatch_queue_t queue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);//建立了一個並行隊列 dispatch_queue_t queue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);//建立了一個串行隊列 複製代碼
Main Dispatch Queue
是串行隊列。加入到主隊列中的任務必定不會生成新的線程,由於主隊列必須有且只有一條主線程。iOS8.0以後的權限markdown
名稱 | 描述 |
---|---|
QOS_CLASS_USER_INTERACTIVE | 與用戶交互的任務,這些任務一般跟UI級別的刷新相關,好比動畫,這些任務須要在一瞬間完成 |
QOS_CLASS_USER_INITIATED | 由用戶發起的而且須要當即獲得結果的任務,好比滑動scroll view時去加載數據用於後續cell的顯示,這些任務一般跟後續的用戶交互相關,在幾秒或者更短的時間內完成 |
QOS_CLASS_DEFAULT | 優先級介於user-initiated 和 utility,當沒有 QoS信息時默認使用,開發者不該該使用這個值來設置本身的任務 |
QOS_CLASS_UTILITY | 一些可能須要花點時間的任務,這些任務不須要立刻返回結果,好比下載的任務,這些任務可能花費幾秒或者幾分鐘的時間 |
QOS_CLASS_BACKGROUND | 這些任務對用戶不可見,好比後臺進行備份的操做,這些任務可能須要較長的時間,幾分鐘甚至幾個小時 |
QOS_CLASS_UNSPECIFIED | 未指定 |
dispatch_group是GCD的一項特性,可以把任務分組。調用者能夠等待這組任務執行完畢,也能夠提供回調函數以後繼續往下執行,這組任務完成時,調用者會獲得通知。經常使用場景好比說,下載一個大的文件,分塊下載,所有下載完成後再合成一個文件。再好比同時下載多個圖片,監聽所有下載完後的動做多線程
dispatch_group_t group = dispatch_group_create();併發
添加任務app
dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);異步
將一個任務添加到指定group中async
dispatch_group_enter(dispatch_group_t group); dispatch_group_leave(dispatch_group_t group);函數
這兩個函數同上邊同樣的效果,不過必定要注意這兩個函數必須成對出現!不然這一組任務就永遠執行不完。
監放任務完成
timeout參數表示函數在等待dispatch group執行完畢後,應該阻塞多久。若是執行dispatch group所需的時間小於timeout,則返回0,不然返回非0值.此參數能夠取常量DISPATCH_TIME_FOREVER,這表示函數會一直等着dispatch group 執行完,而不會超時。此方法會阻塞線程。
*** 使用dispatch_group_notify***
//這個例子演示將將一個很大的字符串刪除指定幾個字符串。好比「abcdefghicladedgdsfs」刪除"ace"三個字符串
int const COUNT = 6;
@interface ViewController ()
{
NSMutableArray *values;
dispatch_group_t group;
dispatch_queue_t queue;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
values = [NSMutableArray array];
group = dispatch_group_create();
queue = dispatch_queue_create("com.aa.test", DISPATCH_QUEUE_CONCURRENT);
[self removeChars:@"abcdefghicladedgdsfs" target:@"ace"];
dispatch_group_notify(group, queue, ^{
NSString *str = [values componentsJoinedByString:@""];
NSLog(@"%@",str);
});
}
- (void) removeChars:(NSString *)pBuffer target:(NSString *)target {
for (int i = 0; i<COUNT; i++) {
if (i==COUNT-1) {
NSString *str = [pBuffer substringWithRange:NSMakeRange(pBuffer.length/(COUNT-1)*i,pBuffer.length-pBuffer.length/(COUNT-1)*i)];
[values addObject:str];
}else{
NSString *str = [pBuffer substringWithRange:NSMakeRange(pBuffer.length/(COUNT-1)*i, pBuffer.length/(COUNT-1))];
[values addObject:str];
}
}
for (int i = 0; i<values.count; i++) {
[self handlerString:values[i] target:target index:i];
}
}
- (void)handlerString:(NSString *)string target:(NSString *)target index:(int)index{
dispatch_group_async(group, queue, ^{
values[index] = [self replace:string target:target];
});
}
- (NSString *)replace:(NSString *)string target:(NSString *)target{
for (int i = 0; i<target.length; i++) {
char temp = [target characterAtIndex:i];
string = [string stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%c", temp] withString:@""];
}
return string;
}
@end
複製代碼
使用dispatch_group_enter和dispatch_group_leave
dispatch_group_t group =dispatch_group_create();
dispatch_queue_t globalQueue=dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
//模擬多線程耗時操做
dispatch_group_async(group, globalQueue, ^{
sleep(3);
NSLog(@"%@---block1結束。。。",[NSThread currentThread]);
dispatch_group_leave(group);
});
NSLog(@"%@---1結束。。。",[NSThread currentThread]);
dispatch_group_enter(group);
//模擬多線程耗時操做
dispatch_group_async(group, globalQueue, ^{
sleep(3);
NSLog(@"%@---block2結束。。。",[NSThread currentThread]);
dispatch_group_leave(group);
});
NSLog(@"%@---2結束。。。",[NSThread currentThread]);
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@---所有結束。。。",[NSThread currentThread]);
});
複製代碼
dispatch_async
和dispatch_sync
dispatch_async
將指定的Block異步
的追加到指定的Dispatch Queue中。dispatch_async
函數不會作任何等待
dispatch_sync
將指定的Block同步
的追加到指定的Dispatch Queue。此時dispatch_sync
會一直等待Block執行結束以後,纔會返回。線程才能接着繼續執行其餘代碼。
如dispatch_group_wait函數同樣,"等待"意味着當前線程中止。
例:
dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"測試2"); }); 複製代碼
執行以上代碼則會直接崩潰。主要是由於dispatch_sync
在追加Block的過程當中同時在等待,等待意味着當前主線程已經中止,因此主線程沒法執行追到到Main Dispatch Queue的Block。
Serial Dispatch Queue也會引發一樣的問題
dispatch_queue_t queue = dispatch_queue_create("com.test.queue", NULL); dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"測試"); }); }); 複製代碼
同樣的道理,串行隊列中只會生成一條線程,而dispatch_sync
函數使該線程處於等待
狀態即中止狀態,因此沒法將Block追加到com.test.queue
這個隊列中。
dispatch_sync
函數
dispatch_sync
,則Block是在主線程上執行的)dispatch_async
表明異步任務,意思不是必定會生成一條線程。若是在MainQueue中執行,則不會生成線程;若是在Global Queue中有可能會生成。由於線程有一個線程池,會重用已經完成任務了的線程。
做用:與併發隊列結合,能夠高效率的避免數據競爭的問題
相同點:dispatch_barrier_sync
和dispatch_barrier_async
函數功能同樣就是在併發隊列中將此代碼插入的地方上下隔開,若是柵欄同樣,兩部分不影響。只有上邊的併發隊列都執行結束以後,下邊的併發隊列纔可以執行。
不一樣點:dispatch_barrier_sync
代碼後邊的任務直到dispatch_barrier_sync
執行完才能被追加到隊列中;dispatch_barrier_async
不用代碼執行完,後邊的任務也會被追加到隊列中。代碼上的體現就是dispatch_barrier_sync
後邊的代碼不會執行,dispatch_barrier_async
後邊的代碼會執行,可是Block不會被執行。
This function submits a block to a dispatch queue for multiple invocations and waits for all iterations of the task block to complete before returning. If the target queue is a concurrent queue returned by dispatch_get_global_queue, the block can be invoked concurrently, and it must therefore be reentrant-safe. Using this function with a concurrent queue can be useful as an efficient parallel for loop.
該函數指定次數將指定的Block追加到指定的Queue中,並等待所有處理執行結束。
dispatch_apply(10, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^(size_t index) {
});
複製代碼
做用:與併發隊列結合,能夠更有效執行併發任務。
掛起指定的Dispatch Queue
dispatch_suspend(queue);
複製代碼
恢復指定的Dispatch Queue
dispatch_resume(queue);
複製代碼
當計數爲0時等待,計數爲1或大於1時不等待
dispatch_semaphore_create(1);//建立1個信號
複製代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//當計數值大於1時,或者在待機中計數值大於1時,對該計數減1而且返回。
複製代碼
dispatch_semaphore_signal(semaphore);//對計數值加1
複製代碼
單例模式,保證在應用程序中只執行一次指定處理的api。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
複製代碼
NSString *path = [[NSBundle mainBundle] pathForResource:@"iosintroduce" ofType:@"md"]; dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0); dispatch_io_t pipe_chanel = dispatch_io_create_with_path(DISPATCH_IO_STREAM,[path UTF8String], 0, 0,queue , ^(int error) { }); size_t water = 1024; dispatch_io_set_low_water(pipe_chanel, water); dispatch_io_set_high_water(pipe_chanel, water); NSMutableData *totalData = [[NSMutableData alloc] init]; dispatch_io_read(pipe_chanel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t _Nullable data, int error) { if (error == 0) { size_t len = dispatch_data_get_size(data); if (len > 0) { [totalData appendData:(NSData *)data]; } } if (done) { NSString *str = [[NSString alloc] initWithData:totalData encoding:NSUTF8StringEncoding]; NSLog(@"%@", str); } }); 複製代碼
NSString *path = [[NSBundle mainBundle] pathForResource:@"iosintroduce" ofType:@"md"]; dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT); dispatch_fd_t fd = open(path.UTF8String, O_RDONLY); dispatch_io_t io = dispatch_io_create(DISPATCH_IO_RANDOM, fd, queue, ^(int error) { close(fd); }); off_t currentSize = 0; long long fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil].fileSize; size_t offset = 1024*1024; dispatch_group_t group = dispatch_group_create(); NSMutableData *totalData = [[NSMutableData alloc] initWithLength:fileSize]; for (; currentSize <= fileSize; currentSize += offset) { dispatch_group_enter(group); dispatch_io_read(io, currentSize, offset, queue, ^(bool done, dispatch_data_t _Nullable data, int error) { if (error == 0) { size_t len = dispatch_data_get_size(data); if (len > 0) { const void *bytes = NULL; (void)dispatch_data_create_map(data, (const void **)&bytes, &len); [totalData replaceBytesInRange:NSMakeRange(currentSize, len) withBytes:bytes length:len]; } } if (done) { dispatch_group_leave(group); } }); } dispatch_group_notify(group, queue, ^{ NSString *str = [[NSString alloc] initWithData:totalData encoding:NSUTF8StringEncoding]; NSLog(@"%@", str); }); 複製代碼
dispatch_io_create_with_path
經過路徑建立一個dispatch_io_t
。與之類似的也好幾個方法,好比dispatch_io_create_with_io
dispatch_io_set_high_water
和dispatch_io_set_low_water
分割文件大小,分別能夠設置一次最少讀取和一次最多讀取多大。dispatch_io_read
讀取文件。與之對應的是dispatch_io_write
將文件存儲到指定路徑若是想提升文件讀取速度,能夠嘗試使用Dispatch I/O。
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 15ULL*NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1ull*NSEC_PER_SEC); dispatch_source_set_event_handler(timer, ^{ NSLog(@"wake up"); dispatch_source_cancel(timer); }); dispatch_source_set_cancel_handler(timer, ^{ NSLog(@"canceled"); }); dispatch_resume(timer); 複製代碼
延遲執行任務
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
複製代碼
最快3秒後執行,最慢3秒+一個runloop循環時間
改變生成的Dispatch Queue的執行優先級
//將myQueue優先級設置爲與globalQueue相同 dispatch_queue_t myQueue = dispatch_queue_create("com.test.queue", NULL); dispatch_queue_t globalQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0); dispatch_set_target_queue(myQueue, globalQueue) 複製代碼
第一個參數須要變動優先級的Dispatch Queue,第二個參數指定要使用的執行優先級相同優先級的Queue。
已經同步發佈到FlyOceanFish的博客