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
GCD全解析
1
2
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
1
14. Reference
Thread Safety Summary
dispatch_sync死锁问题研究
GCD 深入理解:第一部分
小笨狼漫谈多线程:GCD(一)
Objective-C高级编程读书笔记之GCD
...