iOS多线程--下(GCD)
2016-07-11 17:35
295 查看
1 GCD
它是一种纯C语言,它是为多核并列运算设计的。可以自动管理线程的生命周期。GCD 是面向任务和队列的,不是面向线程的。他有两个关键字“任务”“队列”。
使用 GCD 的步骤主要是:
1 定制任务
2 任务添加到队列中,队列支持 FIFO 原则
#基本形式如下
dispath_queue_t queue = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFULT,0); dispath_async(queue,^{ //任务代码段 });
1.1 任务
1.1.1 同步方式
在当前线程中执行,不具备开启线程的能力。dispath_sync( queue,block); //在队列中执行 block 所定义的任务,以同步的方式
1.1.2 异步方式
在新线程中执行,具备开启新线程的能力dispath_async(queue,block); //在队列中执行 block 所定义的任务,以异步的方式
决定了是否有能力开启新的线程
1.2 队列
1.2.1 并行队列
并行队列,队列中的任务,以并行的方式进行,多任务同时进行。一般情况下,我们使用的是全局的并发队列。获取全局并发队列的方式如下:
//默认的写法,获取全局并发队列。前一个参数是优先级默认,后面一个参数是苹果保留的参数 dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
1.2.2 串行队列
串行队列中,队列中的人物,都是以串行的方式进行。先执行一个任务,再执行另一个任务。串行队列分为两种,一种是手动创建的串行队列,另一个是主队列
1.2.2.1 手动创建队列
//默认写法,第一个参数是队列名称,随便填写;第二个参数是队列属性,一般情况下写 NULL dispatch_queue_create("queueName",NULL);
1.2.2.2 主队列
//默认写法 dispath_get_main_queue(); 主队列的任务执行,都是在主线程中进行的,一般用来做线程间的通信。
2 常见的组合方式
2.1 异步方式+并发队列(最常用)
①会创建新的线程②并发执行任务
下面的例子中,任务1和任务2是并发执行的
//实例代码 //①获取全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); //②异步方式处理任务1 dispatch_async(queue,^{ NSLog("下载图片1--%@",[NSThread currentThread]); }); //②异步方式处理任务2 dispatch_async(queue,^{ NSLog("下载图片2--%@",[NSThread currentThread]); });
2.2 异步方式+串行队列
①会创建新线程,因为是串行方式执行,一般情况下只会新建一条线程。②队列中的任务以串行方式执行:任务1和任务2是串行执行的
//示例代码 //①手动创建串行队列 dispatch_queue_t queue = dispatch_queue_create("queueName",NULL); //②异步方式处理任务1 dispatch_async(queue,^{ NSLog("正在下载图片1---%@",[NSThread currentThread]); }); //②异步方式处理任务2 dispatch_async(queue,^{ NSLog("正在下载图片2---%@",[NSThread currentThread]); });
2.3 同步方式+并发队列
①不会创建线程②并发队列的并发功能消失,队列中的所有任务串行执行。
//示例代码 //①获取全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); //②同步执行任务1 dispatch_sync(queue,^{ NSLog(@"下载图片1---%@",[NSThread currentThread]); }); //②同步执行任务2 dispatch_sync(queue,^{ NSLog(@"下载图片2---%@",[NSThread currentThread]); });
2.4 同步方式+串行队列
①不会创建新线程②串行队列中的所有任务串行执行
//示例代码 //①手动创建串行队列 dispatch_queue_t queue = dispatch_queue_create("queueName",NULL); //②同步执行任务1 dispatch_sync(queue,^{ NSLog(@"正在下载图片1-----%@",[]NSThread currentThread]); }); //②同步执行任务2 dispatch_sync(queue,^{ NSLog(@"正在下载图片2-----%@",[]NSThread currentThread]); });
2.5 主队列+异步方式(主队列也是串行队列)
①主队列是特殊的队列,此时异步方式虽然具备创建新线程的能力,但是实际上并不能创建新线程②所有的任务都是在主队列中串行执行的,也就是在主线程中进行,一般用来做进程之间的通信。
//①获取当前的主队列 dispatch_queue_t queue = dispatch_get_main_queue(); //②异步方式执行 dispatch_async(queue,^{ NSLog(@"正在下载图片---%@",[NSThread currentThread]); });
2.6 主队列+同步方式(主队列也是串行队列)
这种方式,会卡死整个程序,不用当前执行下面的程序是在主线程中执行的,当执行到第二步的时候,需要将下面的任务加入到主队列中串行执行。因为是串行执行,主线程必须等到第二步结束后才能继续向下执行,但是第二步是将任务加载到了主队列的末尾,必须要等到之前主线程队列执行完毕后才能执行,所以陷入了卡死状态。
//1获取主队列 dispatch_queue_t queue = dispatch_get_main_queue(); //2同步执行主队列 dispatch_sync(queue,^{ NSLog(@"正在执行。。。"); });
3 队列的内存管理
需要遵循的原则:* 凡是函数名中带有 create\copy\new\retain 等字眼,都应该在不需要使用这个数据的时候release 操作
* GCD的数据类型,在ARC下不需要再 release
* CF(Core Foundation)的数据类型在 ARC 和 MRC 环境下都需要手动 release。
例子:
* CFRelease(id);
NSDictionary *dict = @{@"1":@"1"}; CFDictionaryRef dictCF = (__bridge CFDictionaryRef)(dict); CFRelease(dictCF);
4 线程之间的通信
举例,点击控制器的 View,在子线程中从网上下载一张图片,下载完毕后在主线程中更新按钮的图片。//1 获取全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //2 异步方式下载图片 dispatch_async(queue, ^{ NSString *str = @"http://u1.img.mobile.sina.cn/public/files/image/600x150_img577f313ec621a.png"; NSURL *url = [NSURL URLWithString:str]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; //3 回到主线程,给 UIButton 设置图片 dispatch_async(dispatch_get_main_queue(), ^{ [self.button setImage:image forState:UIControlStateNormal]; }); });
5 延时执行操作
在程序设计中,延时执行的方式有三种5.1 sleep
想要做延时操作,此种方式只需要在延时执行的操作之前执行如下代码即可/ 4000 /使当前线程睡眠3秒钟后再次执行 [NSThread sleepForTimeInterval:3]
缺点:会卡死当前调用的线程,整个线程会停滞。如果卡死的是主线程,那么意味着 UI 会受很大的影响。
5.2 performSelectorAfter
//当3秒后,执行 download 方法。3秒后在主线程中执行该操作 [self performSelector:@selector(download) withObject:nil afterDelay:3]
5.3 GCD
可以根据队列的类型,决定延时后的操作在哪个线程中执行,可以指定在全局并发队列中执行,也可以指定在主队列中执行。//dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{ NSLog(@"延时操作---%@",[NSThread currentThread]); });
6 一次性代码
某个代码段,在程序运行过程中,不管调用多少次,实际上改代码只执行了一次,这就是一次性代码比如说:点击界面后,开始下载图片。如果下载图片的代码不是一次性代码的话,每点击一次界面,都需要再次下载,显然不符合实际情况。使用其他方式也可以实现(定义变量或者定义标识),但一次性代码是最简单的操作。
static dispatch_once_t oneceToken; dispatch_once(&onceToken,^{ //想要只执行一次的代码 //..... });
7 队列组
队列组就是一个对象,内部包含了队列,当队列中的任务执行完毕后,会自动调用响应的方法,这就是队列组。需求:
需要从网络上下载一张图片,然后再下载一张 logo 图片,用 logo 做水印。如果按照顺序,先下载图片,再下载 logo,最后进行图片水印叠加,会比较耗时。最好的办法是 两张图片分别放到两个线程中进行,当两个图片全部下载好之后,再进行组装。
实现思路:
采用队列组,手动创建一个队列组,使用dispatch_group_async(group,queue,^{})来开辟新线程执行下载操作。当队列组中的队列任务全部结束之后,会自动调用 dispatch_group_notify(queue,^{})函数,也就是说我们可以将合并水印的操作放在这个函数中进行。
示例代码
//1 获取全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY——DEFAULT,0); //2 创建队列组--如果是 MRC 环境中,需要 release操作: dispatch_release(group); dispatch_group_t group = dispatch_group_create(); //3 下载图片1 __block UIImage *image1 = nil; //加__block 修饰是为了 block 中可以访问该变量 dispatch_group_async(group,queue,^{ NSString *str = @"http://image1.png"; NSURL *url = [NSURL urlWithString:str]; NSData *data = [NSData dataWithContentsOfURL:url]; image1 = [UIImage imageWithData:data]; }); //4 下载图片2 __block UIImage *image2 = nil; //加__block 修饰是为了 block 中可以访问该变量 dispatch_group_async(group,queue,^{ NSString *str = @"http://image2.png"; NSURL *url = [NSURL urlWithString:str]; NSData *data = [NSData dataWithContentsOfURL:url]; image2 = [UIImage imageWithData:data]; }); //5 合并图片--group中所有队列的任务执行完之后,自动调用下面的函数 dispatch_group_notify(group,queue,^{ //5.1 开启当前图形上下文 UIGraphicsBeginImageContextWithOptions(image1.size,NO,0.0); //5.2 将 image1画在上下文 [image1 drawInRect:CGRectMake(0,0,image1.size.width,image1..size.height)]; //5.3 将 image2画在上下文 [image2 drawInRect:CGRectMake(0,0,100.50)]; //5.4 获取当前上下文的图片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); //5.6 回到主线程,刷新 UI dispatch_async(dispath_get_main_queue,^{ self.imageView.image = image; }); });
相关文章推荐
- Python3写爬虫(四)多线程实现数据爬取
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 如何组织构建多文件 C 语言程序(二)
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 如何写好 C main 函数
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- C#实现多线程的同步方法实例分析
- Lua和C语言的交互详解
- 浅谈chuck-lua中的多线程
- C#简单多线程同步和优先权用法实例
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#多线程编程中的锁系统(三)
- 解析C#多线程编程中异步多线程的实现及线程池的使用
- C#多线程学习之(六)互斥对象用法实例
- 基于一个应用程序多线程误用的分析详解
- C#多线程学习之(三)生产者和消费者用法分析