基本的 GCD 使用
同步异步 \ 队列 |
全局并行队列 |
手动创建的串行队列 |
主队列 |
同步(sync) |
没有 开启新线程
串行 执行任务 |
没有 开启新线程
串行 执行任务 |
死锁 |
异步(async) |
有 开启新线程
并行 执行任务 |
有 开启新线程
串行 执行任务 |
没有 开启新线程
串行 执行任务 |
串行队列
创建串行队列
在使用 GCD 的时候先写 dispatch
在 C 语言中,定义对象通常是以_t
或者Ref
结尾的
特点:依次顺序执行
在非 ARC 中需要释放队列:dispatch_release(q)
DISPATCH_QUEUE_SERIAL
串行 (宏定义了:#define DISPATCH_QUEUE_SERIAL NULL)
DISPATCH_QUEUE_CONCURRENT
并行
1 2
| dispatch_queue_t q = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
|
创建任务
同步任务
不会开启新的线程
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
串行队列中,同步任务在主线程工作,所有任务顺序执行
1 2 3 4 5
| for (int i = 0; i < 10; i++) { dispatch_sync(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
异步任务
只能开一条线程
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
串行队列中,异步任务最多只能开一条线程,所有任务顺序执行
串行队列,异步任务,在多线程中,是斯坦福大学最推荐的一种多线程方式
优点:将任务放在其他线程中工作,每个任务顺序执行
缺点:并发能力不强,最多只能使用一条线程
1 2 3 4 5
| for (int i = 0; i < 10; i++) { dispatch_async(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
并行队列
创建并行队列
dispatch_queue_t q = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
创建任务
同步任务
不会开启新的线程
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
在实际开发中,同步任务可以保证执行完成之后,才让后续的异步任务开始执行,用于控制任务之间的先后顺序
1 2 3 4 5 6
| NSLog(@"================2.1同步任务================"); for (int i = 0; i < 10; i++) { dispatch_sync(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
异步任务
会在多条线程上工作,具体开多少条线程,由系统决定
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
仍然是按照任务添加到队列中的顺序被调度,知识执行先后可能会有差异
能够将耗时的操作放到子线程中工作
与串行队列相比,开启多条线程(系统决定),并发性能更好,但是执行的先后顺序不固定
1 2 3 4 5
| for (int i = 0; i < 10; i++) { dispatch_async(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
全局并行(并发)队列(使用的更为普遍)
与自定义并行队列的区别就是名字不同,其他完全相同
获取全局队列
与自定义并行队列的区别就是名字不同,其他相同
dispatch_queue_t q = dispatch_get_global_queue(long identifier, unsigned long flags);
关于dispatch_queue_priority_t
1 2 3 4
| #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
|
1 2 3 4
| ```objc dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //等同于dispatch_queue_t q = dispatch_get_global_queue(0, 0);
|
创建任务
同步任务
1 2 3 4 5
| for (int i = 0; i < 10; i++) { dispatch_sync(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
异步任务
1 2 3 4 5
| for (int i = 0; i < 10; i++) { dispatch_async(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
主队列
专门在主线程上工作的队列,不能开线程
获取主线程
1
| dispatch_queue_t q = dispatch_get_main_queue();
|
创建任务
同步任务
注意:不要使用同步任务,会发生死锁
1 2 3 4 5
| for (int i = 0; i < 10; i++) { dispatch_sync(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
异步任务,在主线程上依次顺序执行
1 2 3 4 5
| for (int i = 0; i < 10; i++) { dispatch_async(q, ^{ NSLog(@"%zd %@",i , [NSThread currentThread]); }); }
|
GCD 多线程同步等问题
串行队列中可以将一个任务放到另一个任务完成之后继续,这里讨论并行队列同步问题。
dispatch_barrier_async
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
dispatch_barrier_async
必定在前面创建的任务运行完之后才会调用,并且其之后创建的任务必定在其运行完之后才会运行,与 dispatch_barrier_async
代码 位置 执行时间有关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| dispatch_queue_t concurrentQueue = dispatch_queue_create("tbd.tech", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-1"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-2"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-3"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-4"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-5"); });
dispatch_barrier_async(concurrentQueue, ^(){ NSLog(@"dispatch-barrier"); });
dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-a"); }); dispatch_async(concurrentQueue, ^(){ NSLog(@"dispatch-b"); });
|
dispatch_group_async & dispatch_group_notify
1 2 3 4 5 6 7
| dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
|
dispatch_group_async
是创建 GCD 的 group 操作。在dispatchGroup
中所有任务运行完之后,才会调用dispatch_group_notify
,与代码的顺序无关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| dispatch_queue_t dispatchQueue = dispatch_queue_create("tbd.tech", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-1 : %@", [NSThread currentThread]); }); dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-2 : %@", [NSThread currentThread]); });
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){ NSLog(@"Success : %@", [NSThread currentThread]); });
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-3 : %@", [NSThread currentThread]); }); dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-4 : %@", [NSThread currentThread]); }); dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-5 : %@", [NSThread currentThread]); }); dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-6 : %@", [NSThread currentThread]); });
|
dispatch_apply
1 2
| dispatch_apply(size_t iterations, dispatch_queue_t queue, DISPATCH_NOESCAPE void (^block)(size_t));
|
dispathc_apply
是 dispatch_sync
和dispatch_group
的关联 API.它以指定的次数将指定的 Block 加入到指定的队列中。并等待队列中操作全部完成.
1 2 3 4 5 6 7 8 9 10 11 12 13
| NSArray *array = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10", @"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20"]; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_async(queue, ^{ dispatch_apply(array.count, queue, ^(size_t index){ NSLog(@"%@", array[index]); });
dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"处理完成,回到主线程。"); }); });
|
dispatch_semaphore & @synchronized
要防止对同一数据产生写冲突,就需要设置加锁(@synchronized
)或者采用信号量(dispatch_semaphore
)的方式。
dispatch_semaphore
1 2 3 4 5 6
| dispatch_semaphore_t dispatch_semaphore_create(long value);
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| NSMutableArray *array = [NSMutableArray array]; dispatch_group_t group = dispatch_group_create(); dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); NSDate *date = [[NSDate alloc] init]; for (int index = 0; index < 100000; ++index) { dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [array addObject:[NSNumber numberWithInt:index]]; dispatch_semaphore_signal(semaphore); }); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"%lu", (unsigned long)array.count); double time = [date timeIntervalSinceNow]; NSLog(@"time %f", time); });
|
@synchronized
试验了一下性能不如dispatch_semaphore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| NSMutableArray *array = [NSMutableArray array]; dispatch_group_t group = dispatch_group_create(); NSDate *date = [[NSDate alloc] init]; for (int index = 0; index < 100000; ++index) { dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ @synchronized(array) { [array addObject:[NSNumber numberWithInt:index]]; } }); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"%lu", (unsigned long)array.count); double time = [date timeIntervalSinceNow]; NSLog(@"time %f", time); });
|