小编之前的文章中有所提及关于网页接口 NSURL AND NSURLRequest 会阻塞进程的问题, 从今天开始到五一假期结束期间, 我们来解决关于线程的问题, 此处引用的代码以及 demo 来自斯坦福大学 ios7 开发课程(十), 大学课本《计算机操作系统》以及《Objective-C 高级编程》, 小编特地做以整合.
一. 线程的引入
在 OS 中引入进程的目的是为了使多个程序能并发执行, 以提高资源的利用率和系统的吞吐量, 那么, 在操作系统中再次引入线程, 则是为了减少程序在并发执行时候所付出的时空开销, 更好的使 OS 具有更好的并发性.
为了更好的理解线程, 我们先将它放置一边, 来看一下他的孪生兄弟 -- 进程
1. 进程的两个基本属性
进程是一个可以拥有资源的独立单位, 一个进程要能独立运行, 它必须拥有一定的资源[存放程序正文, 数据的磁盘和内存地址空间以及它在运行时候所需要的 I/O 设备, 已打开的文件, 信息量等]
进程同时又是一个可独立调度和分配等基本单位, 一个进程要能独立运行, 它必须是一个可独立调度和分配的基本单位
图 1-- 进程
2. 程序并发时候所需要的时空开销
Step1: 创建进程, 创建进程的时候, 我们需要进行为进程分配所必须的内存空间, 除了 CPU 以外的所有资源: 内存空间, I/O 设备以及建立相应的 PCB;
Step2: 撤销进程, 撤销进程时候, 需要先释放处理机的所有资源进行回收, 再撤销 PCB;
Step3: 进程切换, 进程在进行上下文切换的同时, 需要保留当前进程的 CPU 环境, 然后在创建新的 CPU 环境, 这个时候会占有大量的时间空间
综上来看, 我们发现了进程在创建, 销毁和进程上下文切换后会占用大量的时空开销, 为次, 引出了线程的概念
二. iOS 线程的讲解
1.Grand Central Dispatch(GCD)概要
Grand Central Dispatch(GCD)是异步执行的任务的技术之一, 一般将应用程序中记述的线程管理用的代码在系统级中实现, 开发者只需要定义想执行的任务并追加到适当的 Dispatch Queue 中, GCD 就能生成必要的线程并执行任务, 由于线程管理是作为系统的一部分来实现的, 因此可统一管理, 也可以执行任务
导入 GCD 代码展示
- dispatch_async(queue,^{
- /* 长时间处理的任务 */
- dispatch_async(dispatch_get_main_queue(),^{
- /* 只在主线程可以执行的处理
- * 例如用户界面更新 */
- )};
- });
分析: 通过异步调用进行处理多线程的问题
dispatch_async(queue,^{}); 表示让处理在后台线程中执行
dispatch_async(dispatch_get_main_queue(),^{}); 表示让 Block 内容在主线程中执行
在除了主线程的其他队列中, 我们主要执行复杂的数据运算与更新, 而只在主线程中进行更新用户的 UI 界面, 这样在显示 UI 界面的同时实现数据的更新和处理, 使得宏观上并行操作
- 2.Dispatch Queue
- dispatch_async(queue,^{
- /* 想要执行的任务 */
- });
分析: 该源码通过使用 Block 语法 "定义想要执行的任务", 通过 dispatch_async 函数追加赋值在变量 queue 的 "Dispatch Queue 中", 这样就可以使指定的 Block 在另一个线程中执行
Dispatch Queue --> 执行处理等待队列 (先进先出) 执行处理
图片 2-- 线程处理顺序
分类:
- Dispatch Queue --> 1.Serial Dispatch Queue(等待现在执行中处理)
- --> 2.Concurrent Dispatch Queue (不等待现在执行中处理)
示意图
图 3--Serial Dispatch Queue
图 4--Concurrent Dispatch Queue
- dispatch_async(queue, blk0);
- dispatch_async(queue, blk1);
- dispatch_async(queue, blk2);
- dispatch_async(queue, blk3);
- dispatch_async(queue, blk4);
- dispatch_async(queue, blk5);
- dispatch_async(queue, blk6);
- dispatch_async(queue, blk7);
运行结果: 顺序执行
- blk0;
- blk1;
- blk2;
- blk3;
- blk4;
- blk5;
- blk6;
- blk7;
当变量 queue 为 Concurrent Dispatch Queue 时候, 因为不需要等待现在执行的处理结束, 所以首先执行 blk0, 不管 blk0 是否结束, 都会执行下面的 blk1, 如此重复下去, 直到执行完 blk7
因此, 我们可以通过示意图理解 Concurrent Dispatch Queue 使用多个线程, Serial Dispatch Queue 使用一个线程
图 5-- 线程关系图
通过对多线程简单了解, 我们将概念性的知识点先放在一旁, 来通过斯坦福大学的 ios7 开发部分代码更好的理解一下 dispatch_async 的应用
- NSURLRequest *request = [NSURLRequest requestWithURL:self.imageURL];
- NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
- NSURLSession *sesstion = [NSURLSession sessionWithConfiguration:configuration];
- NSURLSessionDownloadTask *task = [sesstion downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable locatfile, NSURLResponse * _Nullable response, NSError * _Nullable error) {
- if (!error) {
- if ([request.URL isEqual:self.imageURL]) {
- UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:locatfile]];
- dispatch_async(dispatch_get_main_queue(), ^{
- self.image = image;
- });
- }
- }
- }];
- [task resume];
分析:- NSURLRequest *request = [NSURLRequest requestWithURL:self.imageURL]; 通过 NSURLRequest 来进行网页请求
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *sesstion = [NSURLSession sessionWithConfiguration:configuration]; 通过 NSURLSessionConfiguration 进行网页配置并且将消息发送给了 NSURLSession
我们通过网页下载作业, 将下载的网页保存在 locatfile 地址中, 并且下载成功, 我们更新 UIImage 视图, 与此同时, 我们的主线程来更新 self.image 的 UI 更新
- if ([request.URL isEqual:self.imageURL]) {
- UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:locatfile]];
- dispatch_async(dispatch_get_main_queue(), ^{
- self.image = image;
- });
- }
这一部分很好的对多线程进行了理解
总结, 本章简要分析了多线程, 后期的文章将会对多线程进行更加详细的分析, 大家对于多线程有什么问题可以在下方留言, 后期会整合在文章中进行讲解.
来源: http://www.jianshu.com/p/b1e6be5cd2fe