文明看博转载是对自己的尊重也是对学者的鼓励,欢迎讨论
iOS-GCD多线程编程详解
一.前言
前面的多线程编程中分别讲到NSThread和NSOperation的多线程编程,本张主要是讲述GCD的编程,GCD的多线程编程是基于BLock或者函数包裹多线程任务,由系统管理线程实现的,因此其低耦合,容易使用,又是基于C语言实现,运行速度快,是官方推荐的多线程实现的首先方案,至于个人什么顺手就用什么。
二.GCD多线程使用说明
在多线程编程中,首先要考虑的问题是多线程任务写在什么地方,同步还是异步,是在主线程中执行还是在子线程中执行,多线程任务的依赖关系,执行的先后顺序,是自己创建线程还是系统的线程承载线程任务的执行,是自己管理线程还是系统管理,对于GCD而言,多线程任务通过Block或者函数来包含多线程任务,它的依赖和执行顺序有特定的GCD函数,它的任务承载线程是有系统生成和管理,所以用户只需要关心多线程任务是使用block还是函数包裹,是使用同步还是异步,是在主线程上执行还是在子线程上执行。
1.dispathc_sync:
该函数的作用是提交一个block到指定的dispatch_queue中,直到block执行结束返回,在主线程中使用该函数就会造成UI卡住:
note:下面这段代码是在主线程中调用
NSLog(@"before dispatch_sync"); //主线程中调用 dispatch_sync(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"hello world"); //这条语句没有被执行 }); });运行结果:
2015-01-03 17:45:58.213 GCDTest[1750:140657] before dispatch_sync(还有就是界面卡死了,因为形成了死锁)
note:下面的这段代码是在非主线程中调用:
@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(netThread:) toTarget:self withObject:@"new thread"]; //子线程分支 NSLog(@"hello world main"); }-(void)netThread:(id)sender{ NSLog(@"before dispatch_sync"); dispatch_sync(dispatch_get_main_queue(), ^{ //相当于在主线程中调用dispatch_sync dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"hello world subThread"); //从结果中看出这条语句没有被运行 }); });}@end运行结果:
2015-01-04 17:37:04.426 GCDTest[1869:155469] before dispatch_sync(屏幕卡死)
2015-01-04 17:37:04.426 GCDTest[1869:155423] hello world main
所以在使用disatich_sync时不要在UI线程使用。
相似函数:
dispatch_sync_f:该函数提交的是一个函数任务,相当于dispatch_sync:提交block,说白了把提交的function相当于block,有兴趣的可以看我其中的一篇BLock揭秘,了解函数和block的一些映射个人思想。
dispatch_async_f(dispatch_get_main_queue(), nil, fun_my); }static void fun_my(void *hel){ NSLog(@"function");}运行结果:
2015-01-04 00:53:48.894 GCDTest[2010:161173] function
在主线程中使用的dispatch_sync(dispatch_get_main_queue,^{})形成死锁图解:
{
dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"我是异步的block1,我被装在dispatch_get_main_queue中"); }); NSLog(@"我是主线程,我的源代码位置被写在block1的后面");
}运行结果:
2015-01-04 22:37:31.821 GCDTest[713:28383] 我是主线程,我的源代码位置被写在block1的后面
2015-01-04 22:37:31.836 GCDTest[713:28383] 我是异步的block1,我被装在dispatch_get_main_queue中
图解分析:
三.各个GCD函数使用详解
通过上一节的简单使用,我们可以把GCD分解为线程任务包装:block,function,线程任务的容器:dispatch_queue(序列类型和并发类型),同步和异步(重点是异步的实现);block可以参考,function就是一般的函数,同步和异步就是同步执行和异步执行,下面通过代码来了解block的容器-------dispatch_queue.
1.dispatch_global_queue
dispatch_group_t group2 = dispatch_group_create(); dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{ NSLog(@"*block1 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{ NSLog(@"*block2 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{ NSLog(@"*block3 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{ NSLog(@"*block4 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group2, dispatch_get_global_queue(0, 0), ^{ NSLog(@"*block5 运行在线程%@",[NSThread currentThread]); });运行结果:
1:
2015-01-04 23:04:37.273 GCDTest[850:36850] *block2 运行在线程<NSThread: 0x7fc2d9c6e350>{number = 5, name = (null)}
2015-01-04 23:04:37.273 GCDTest[850:36848] *block3 运行在线程<NSThread: 0x7fc2d9c6b200>{number = 3, name = (null)}
2015-01-04 23:04:37.273 GCDTest[850:36849] *block4 运行在线程<NSThread: 0x7fc2d9f269a0>{number = 4, name = (null)}
2015-01-04 23:04:37.274 GCDTest[850:36854] *block5 运行在线程<NSThread: 0x7fc2d9c6d180>{number = 6, name = (null)}
2015-01-04 23:04:37.273 GCDTest[850:36847] *block1 运行在线程<NSThread: 0x7fc2d9d1ce30>{number = 2, name = (null)}
2:
2015-01-04 23:07:09.728 GCDTest[858:37701] *block2 运行在线程<NSThread: 0x7fc569406f40>{number = 4, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37700] *block3 运行在线程<NSThread: 0x7fc56940da90>{number = 5, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37698] *block1 运行在线程<NSThread: 0x7fc5694182a0>{number = 2, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37699] *block4 运行在线程<NSThread: 0x7fc56952c740>{number = 3, name = (null)}
2015-01-04 23:07:09.728 GCDTest[858:37704] *block5 运行在线程<NSThread: 0x7fc569713820>{number = 6, name = (null)}
。。。运行结果每次都不同,所在的运行线程也不同,所以dispatch_global_queue是并行的系统维护的线程依次冲队列中取出block执行
2.dispatch_main_queue
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_main_queue(), ^{ NSLog(@"block1 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_main_queue(), ^{ NSLog(@"block2 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_main_queue(), ^{ NSLog(@"block3 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_main_queue(), ^{ NSLog(@"block4 运行在线程%@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_main_queue(), ^{ NSLog(@"block5 运行在线程%@",[NSThread currentThread]); });运行结果:
2015-01-04 23:07:09.762 GCDTest[858:37660] block1 运行在线程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.762 GCDTest[858:37660] block2 运行在线程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.762 GCDTest[858:37660] block3 运行在线程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.762 GCDTest[858:37660] block4 运行在线程<NSThread: 0x7fc569613060>{number = 1, name = main}
2015-01-04 23:07:09.763 GCDTest[858:37660] block5 运行在线程<NSThread: 0x7fc569613060>{number = 1, name = main}
运行结果每次都是一致的,说明dispatch_main_queue是根据其加入顺序执行的执行的线程为主线程;
所以顺序队列和并行队列的对比图如下:
小结:这篇博文主要说了关于顺序队列和并行队列的block容器的特性,后面将会陆续的对GCD的多线程编程有详细的使用。
引用或者转载请注明出处,是对自己的尊重也是对学者的鼓励。