0. Description
- iOS SDK >= 6.0且開啟ARC,GCD對象就不應該使用dispatch_retain和dispatch_release了(全局隊列任何時候都不需要retain、release),GCD對象可用strong修飾。
- Concurrency & Parallelism
并發的意思就是同時運行多個任務,這些任務可能是在單核 CPU 上以分時(時間共享)的形式同時運行,也可能是在多核 CPU 上以真正的并行方式來運行。然后為了使單核設備也能實現這一點,并發任務必須先運行一個線程,執行一個上下文切換,然后運行另一個線程或進程。并行則是真正意義上的多任務同時運行。 - Context Switch
上下文切換,一個上下文切換指當你在單個進程里切換執行不同的線程時存儲與恢復執行狀態的過程。這個過程在編寫多任務應用時很普遍,但會帶來一些額外的開銷。
1. Queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t customSerialQueue = dispatch_queue_create("customSerialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t customConcurrentQueue = dispatch_queue_create("customConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
2. Normal usage
- 2.1 sync mainQueue
//sync mainQueue : 阻塞當前線程,block在主線程中執行,執行結束后返回。
dispatch_sync(mainQueue, ^{
NSLog(@"sync mainQueue %@", [NSThread currentThread]);
});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死鎖情況1~~使用sync時,當前queue和執行queue相同會造成死鎖,并行queue不會
//死鎖情況2~~在死鎖1的情況下嵌套一個sync,同樣會發生死鎖,如下:
dispatch_sync(customSerialQueue或者globalQueue或者customConcurrentQueue, ^{ //嵌套的sync
dispatch_sync(mainQueue, ^{
NSLog(@"sync mainQueue %@", [NSThread currentThread]);
});
});
- 2.2 async mainQueue
//async mainQueue : 不阻塞當前線程,直接返回,block在主線程中執行
dispatch_async(mainQueue, ^{
NSLog(@"async mainQueue %@", [NSThread currentThread]);
});
- 2.3 sync customSerialQueue
//sync customSerialQueue : 阻塞當前線程,block在當前線程中執行,執行結束后返回。
dispatch_sync(customSerialQueue, ^{
NSLog(@"sync customSerialQueue %@", [NSThread currentThread]);
});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死鎖情況1~~使用sync時,當前queue和執行queue相同會造成死鎖,并行queue不會
//死鎖情況2~~在死鎖1的情況下嵌套一個sync,同樣會發生死鎖,如下:
dispatch_async(customSerialQueue, ^{
dispatch_sync(mainQueue或者globalQueue或者customConcurrentQueue, ^{ //嵌套的sync
dispatch_sync(customSerialQueue, ^{
NSLog(@"sync customSerialQueue %@", [NSThread currentThread]);
});
});
});
- 2.4 async customSerialQueue
//async customSerialQueue : 不阻塞當前線程,直接返回,block在一個新的線程中執行。(如果指定同一個customSerialQueue,則只會在唯一的新線程中執行;如果指定不同的customSerialQueue,則會生成多個新線程)
dispatch_async(customSerialQueue, ^{
NSLog(@"async customSerialQueue %@", [NSThread currentThread]);
});
- 2.5 sync globalQueue
//sync globalQueue : 阻塞當前線程,block在當前線程中執行,執行結束后返回。
dispatch_sync(globalQueue, ^{
NSLog(@"sync globalQueue %@", [NSThread currentThread]);
});
- 2.6 async globalQueue
//async globalQueue : 不阻塞當前線程,直接返回,block每次都在一個新的線程中執行
dispatch_async(globalQueue, ^{
NSLog(@"async globalQueue %@", [NSThread currentThread]);
});
- 2.7 sync customConcurrentQueue
//sync customConcurrentQueue : 阻塞當前線程,block在當前線程中執行,執行結束后返回。
dispatch_sync(customConcurrentQueue, ^{
NSLog(@"sync customConcurrentQueue %@", [NSThread currentThread]);
});
- 2.8 async customConcurrentQueue
//async customConcurrentQueue : 不阻塞當前線程,直接返回,block每次都在一個新的線程中執行
dispatch_async(customConcurrentQueue, ^{
NSLog(@"async customConcurrentQueue %@", [NSThread currentThread]);
});
3. Dispatch group
如果要在queue中的所有任務都結束后做某件事情,由于除去2.6、2.8以外的方式都在單一的線程中執行任務,所以把這件任務最后添加即可。2.6、2.8的方式是在多個線程中執行任務,為了達到同步的目的就可以使用Dispatch group。
- 3.1 異步等待任務完成
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t cQueue = dispatch_queue_create("cQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, cQueue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"1 %@", [NSThread currentThread]);
});
dispatch_group_async(group, cQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"2 %@", [NSThread currentThread]);
});
dispatch_group_async(group, cQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"3 %@", [NSThread currentThread]);
});
//dispatch_group_notify 異步等待任務完成
//notifyQueue為mainQueue則block在主線程中執行,notifyQueue為其他則block在非主線程中執行
dispatch_group_notify(group, notifyQueue, ^{
NSLog(@"4 %@", [NSThread currentThread]);
});
//運行結果:1 3 2 4
- 3.2 阻塞當前線程等待
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t cQueue = dispatch_queue_create("cQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, cQueue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"1 %@", [NSThread currentThread]);
});
dispatch_group_async(group, cQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"2 %@", [NSThread currentThread]);
});
dispatch_group_async(group, cQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"3 %@", [NSThread currentThread]);
});
//dispatch_group_wait 阻塞當前線程,阻塞時間在規定時間和任務執行時間中取最小值,但無論如何都會完成所有任務
long wt = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
if(wt == 0) {
NSLog(@"group中任務執行時間不到規定時間");
}
else {
NSLog(@"group中任務執行時間超過規定時間");
}
//運行結果:如果規定時間為2秒,則為1 group中任務執行時間超過規定時間 3 2;
如果規定時間為5秒,則為1 3 2 group中任務執行時間不到規定時間
- 3.3 dispatch_group_async的等價實現
以下兩種實現是等價的
dispatch_group_async(group, cQueue, ^{
});
dispatch_group_enter(group);
dispatch_async(cQueue, ^{
dispatch_group_leave(group);
});
dispatch_group_enter()和dispatch_group_leave()需要成對出現。
4. Dispatch block
- 4.1 dispatch_block_t
sync customSerialQueue的例子
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"do something");
});
dispatch_sync(customSerialQueue, block);
- 4.2 dispatch_block_wait
會阻塞當前線程,阻塞時間在規定時間和block執行時間中取最小值,但無論如何都會完成所有任務。
dispatch_queue_t customSerialQueue = dispatch_queue_create("customSerialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
[NSThread sleepForTimeInterval:3];
});
dispatch_async(customSerialQueue, block);
long wt = dispatch_block_wait(block, dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC));
if(wt == 0)
{
NSLog(@"block執行時間不到規定時間");
}
else
{
NSLog(@"block執行時間超過規定時間");
}
將DISPATCH_TIME_NOW替換成DISPATCH_TIME_FOREVER,則會一直等待,不會超時。
- 4.3 dispatch_block_notify
dispatch_queue_t customConcurrentQueue = dispatch_queue_create("customConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t previousBlock = dispatch_block_create(0, ^{
[NSThread sleepForTimeInterval:2];
});
dispatch_async(customConcurrentQueue, previousBlock);
dispatch_block_t notifyBlock = dispatch_block_create(0, ^{
NSLog(@"previousBlock done");
});
//dispatch_block_notify 異步
//當previousBlock執行完畢后,提交notifyBlock到notifyQueue中執行,notifyQueue為mainQueue則block在主線程中執行,notifyQueue為其他則block在非主線程中執行
dispatch_block_notify(previousBlock, notifyQueue, notifyBlock);
- 4.4 dispatch_block_cancel
可以取消提交到隊列的block,對已經開始執行的無效
dispatch_async(someQueue, block);
dispatch_block_cancel(block);
5. dispatch_after
異步非阻塞,延遲執行
dispatch_time_t disTime = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC);
//queue為mainQueue在主線程中執行,其他在非主線程中執行
dispatch_after(disTime, queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
6. dispatch_apply
dispatch_apply為同步會阻塞線程,會將block重復執行n次。
- 6.1 在mainQueue中執行
//block都在主線程中執行
dispatch_apply(4, mainQueue, ^(size_t i) {
[NSThread sleepForTimeInterval:(4-i)];
NSLog(@"%zu %@", i, [NSThread currentThread]);
});
NSLog(@"done");
//運行結果:0 1 2 3 done
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死鎖情況1~~如果當前queue和執行queue都是mainQueue會造成死鎖
//死鎖情況2~~在死鎖1的情況下嵌套一個sync,同樣會發生死鎖,如下:
dispatch_sync(customSerialQueue或者globalQueue或者customConcurrentQueue, ^{ //嵌套的sync
dispatch_apply(4, mainQueue, ^(size_t i) {
[NSThread sleepForTimeInterval:(4-i)];
NSLog(@"%zu %@", i, [NSThread currentThread]);
});
NSLog(@"done");
});
- 6.2 在customSerialQueue中執行
//block都在當前線程中執行
dispatch_apply(4, customSerialQueue, ^(size_t i) {
[NSThread sleepForTimeInterval:(4-i)];
NSLog(@"%zu %@", i, [NSThread currentThread]);
});
NSLog(@"done");
//運行結果:0 1 2 3 done
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//死鎖情況1~~如果當前queue和執行queue相同會造成死鎖
//死鎖情況2~~在死鎖1的情況下嵌套一個sync,同樣會發生死鎖,如下:
dispatch_async(customSerialQueue, ^{
dispatch_sync(mainQueue或者globalQueue或者customConcurrentQueue, ^{ //嵌套的sync
dispatch_apply(4, customSerialQueue, ^(size_t i) {
[NSThread sleepForTimeInterval:(4-i)];
NSLog(@"%zu %@", i, [NSThread currentThread]);
});
NSLog(@"done");
});
});
- 6.3 在globalQueue中執行
//block在多個線程中并發執行
dispatch_apply(4, globalQueue, ^(size_t i) {
[NSThread sleepForTimeInterval:(4-i)];
NSLog(@"%zu %@", i, [NSThread currentThread]);
});
NSLog(@"done");
//運行結果:3 2 1 0 done
- 6.4 在customConcurrentQueue中執行
//如果當前queue是customConcurrentQueue,則block都在當前線程中執行
//如果當前queue不是customConcurrentQueue,則block在多個線程中并發執行
dispatch_apply(4, customConcurrentQueue, ^(size_t i) {
[NSThread sleepForTimeInterval:(4-i)];
NSLog(@"%zu %@", i, [NSThread currentThread]);
});
NSLog(@"done");
//運行結果:如果當前queue是customConcurrentQueue,則0 1 2 3 done
如果當前queue不是customConcurrentQueue,則3 2 1 0 done
7. dispatch_once
整個程序運行中只會執行一次,徹底保證線程安全,用在單例模式上是墜吼的。
+ (instancetype)shareSingleTon{
static SingleTon *shareSingleTonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareSingleTonInstance = [[SingleTon alloc] init];
});
return shareSingleTonInstance;
}
8. Dispatch barrier
queue需要為自建的customConcurrentQueue,其他情況都不適用(如果queue為globalQueue,則dispatch_barrier_async等價于dispatch_async,dispatch_barrier_sync等價于dispatch_sync)。
- 8.1 dispatch_barrier_async
不阻塞當前線程,確保customConcurrentQueue中之前插入的任務全部執行完自己再執行,確保自己執行完再執行之后插入的任務。
dispatch_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"1 %@", [NSThread currentThread]); //新的線程
});
dispatch_barrier_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"2 %@", [NSThread currentThread]); //async+customConcurrentQueue:新的線程
});
NSLog(@"3");
dispatch_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"4 %@", [NSThread currentThread]); //新的線程
});
NSLog(@"5");
//輸出:3 5 1 2 4
dispatch_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"1 %@", [NSThread currentThread]); //新的線程
});
dispatch_barrier_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"2 %@", [NSThread currentThread]); //async+customConcurrentQueue:新的線程
});
NSLog(@"3");
dispatch_sync(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"4 %@", [NSThread currentThread]); //當前線程
});
NSLog(@"5");
//輸出:3 1 2 4 5
- 8.2 dispatch_barrier_sync
阻塞當前線程,確保customConcurrentQueue中之前插入的任務全部執行完自己再執行,確保自己執行完再執行之后插入的任務。
dispatch_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"1 %@", [NSThread currentThread]); //新的線程
});
dispatch_barrier_sync(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"2 %@", [NSThread currentThread]); //sync+customConcurrentQueue:當前線程
});
NSLog(@"3");
dispatch_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"4 %@", [NSThread currentThread]); //新的線程
});
NSLog(@"5");
//輸出:1 2 3 5 4
dispatch_async(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"1 %@", [NSThread currentThread]); //新的線程
});
dispatch_barrier_sync(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"2 %@", [NSThread currentThread]); //sync+customConcurrentQueue:當前線程
});
NSLog(@"3");
dispatch_sync(customConcurrentQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"4 %@", [NSThread currentThread]); //當前線程
});
NSLog(@"5");
//輸出:1 2 3 4 5
9. dispatch_set_target_queue
10. Dispatch source
11. Dispatch context
- 11.1 dispatch_set_context
- 11.2 dispatch_get_context
12. dispatch_async_f dispatch_sync_f
13. dispatch_semaphore
14. Reference
Thread Safety Summary
dispatch_sync死鎖問題研究
GCD 深入理解:第一部分
小笨狼漫談多線程:GCD(一)
Objective-C高級編程讀書筆記之GCD
...