新浪微博 有道云笔记 麦库 EverNote Pocket Instapaper 更多

多线程---iOS-Apple苹果官方文档翻译

本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址(2013年12月29日更新版)

 

page1image576.png
多线程 

技术博客http://www.cnblogs.com/ChenYilong/ 新浪微博http://weibo.com/luohanchenyilong
page1image2200.png page1image2360.png
多线程的应用 
 耗时操作,例如网络图片视频歌曲书籍等资源下载  游戏中的声音播放
page2image1480.png 
page2image1640.png
 
page3image432.png
 
page3image592.png
多线程示意图 
 充分发挥多核处理器的优势,并发(同时执行) 执行任务让系统运行的更快、更流畅
page4image1440.png
 
page4image1600.png
进程与线程概念 
  一个运行的程序就是一个进程或者叫做一个任务 
  一个进程至少包含一个线程,线程是程序的执行流 
  iOS程序启动时,在创建一个进程的同时, 会开始运行一个线程,该 线程被称为主线程 
  主线程是其他线程最终的父线程,所有界面的显示操作必须在主线程 进行    
  后台线程无法更新UI界面和响应用户点击事件 
  系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中 
的多个线程则共用进程的内存空间 
  每创建一个新的线程,都会消耗一定内存和CPU时间 
  当多个线程对同一个资源出现争夺的时候需要注意线程安全问题 
page5image3944.png
多线程的优势与难点 
 优势
  充分发挥多核处理器优势,将不同线程任务分配给不同的处 
   理器,真正进入“并行运算”状态

  耗时轮询或者并发需求高等任务分配到其他线程执行, 并由主线程负责统一更新界面会使得应用程序更加流畅,用 户体验更好 
  当硬件处理器的数量增加,程序会运行更快,而无需做任何 调整 
 难点 
  共享资源的“争夺” 
  多线程是为了同步完成多项任务,不是为了提高运行效率, 而是为了通过提高资源使用效率来提高系统的整体性能 
page6image3896.png
多线程使用注意事项 
 线程使用不是无节制的 iOS中的主线程的堆栈大小是1M
 从第二个线程开始都是512KB
 这些数值不能通过编译器开关或线程API函数更改
 只有主线程有直接修改UI的能力
page7image2136.png
iOS的三种多线程技术 

1. NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线
程)
2. 以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去 关心线程的具体使用问题
.1.  NSOperation/NSOperationQueue 面向对象的线程技术 
.2.  GCD —— Grand Central Dispatch(派发) 是基于C语言的框架,可以充分利用多 
     核,是苹果推荐使用的多线程技术
.
以上这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高 的使用越简单,也是Apple最推荐使用的。但是就目前而言,iOS的开发者, 需要了解三种多线程技术的基本使用过程。因为很多框架技术分别使用 了不同多线程技术。
page8image3688.png
三种多线程技术的对比 
 NSThread:
  优点:NSThread 比其他两个轻量级,使用简单 
  缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以 及唤醒等。线程同步对数据的加锁会有一定的系统开销 
 NSOperation: 
  不需要关心线程管理,数据同步的事情,可以把精力放在自己需 
要执行的操作上 
  NSOperation是面向对象的  GCD: 
  Grand Central Dispatch由苹果开发的一个多核编程的解决方案 iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大 的技术 
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
  GCD是基于C语言的 
page9image4392.png
演练(1)NSObject的多线程方法——准备 
.1.  创建一个耗时较长的操作 
.2.  创建一个耗时较短的操作 
.3.  在界面中心放置两个按钮,分别调用 这两个任务 
.4.  使用[NSThread currentThread]分别打 印各个任务所在的线程 
.5.  运行观察效果 
  提示: 
  NSLog是一个相当耗时的操作 在应用 程序正式发布前,一定记住需要对应用 中的NSLog方法进行处理 
  无论使用哪一种多线程技术,均可以使 用[NSThread currentThread]查看当前任 务所在线程 
page10image4312.png 
page10image4472.png
 
page11image432.png
 
page11image592.png
NSObject的多线程方法——后台线程
- (void)performSelectorInBackground:(SEL)aSelector
withObject:(id)arg 
  通常,由于线程管理相对比较繁琐,而很多耗时的任务又无法知道其准 确的完成时间,因此可以使用performSelectorInBackground方法 直接新建一个后台线程,并将选择器指定的任务在后台线程执行,而无 需关心具体的NSThread对象  
  提示: 
 performSelectorInBackground方法本身是在主线程中执行的, 
而选择器指定的方法是在后台线程中进行的 
 使用performSelectorInBackground方法调用的任务可以更新 
UI界面 在大型交互式游戏中,通常使用此方法在后台线程播放音效  
page12image3520.png
@autoreleasepool 
 内存管理对于多线程非常重要
 Objective-C可以凭借@autoreleasepool使用内存资源,并需要时回 收资源
 每个线程都需要有@autoreleasepool,否则可能会出现内存泄漏 
page13image1912.png
NSObject的多线程方法——主线程
- (void)performSelectorOnMainThread:(SEL)aSelector
withObject:(id)arg waitUntilDone:(BOOL)wait; 
 如果要更新UI界面,可以在后台线程中调用
performSelectorOnMainThread方法 
 提示:尽管使用performSelectorInBackground方法调用的任务 可以更新UI界面,但是在实际开发中,涉及到UI界面的更新操作,还 是要使用performSelectorOnMainThread方法,以避免不必要的 麻烦
page14image2640.png
NSObject的多线程小结 
 开启后台执行任务的方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
 在后台线程中通知主线程执行任务的方法 (void)performSelectorOnMainThread:(SEL)aSelector
withObject:(id)arg waitUntilDone:(BOOL)wait;  获取线程信息
[NSThread currentThread];  线程休眠
   [NSThread sleepForTimeInterval:1.0f];
 特点: 使用简单,量级轻 不能控制线程的执行顺序 
page15image4016.png
NSThread  创建线程方法: 
1. + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; 
2. - (id)initWithTarget:(id)target selector: (SEL)selector object:(id)argument; 
 参数说明: selector:线程执行的方法,只能有一个参数,不能有返回值 
 target:selector消息发送的对象  argument:传输给target的唯一参数,也可以是nil
page16image3216.png
NSThread演练——加载图片 
 detachNewThreadSelector
方法会直接启动线程方法 
 initWithTarget需要调用 start方法才能够启动线程方 法 
 
page17image2280.png
 
page17image2440.png
NSOperation & NSOperationQueue
 NSOperation的两个子类1. NSInvocationOperation 2. NSBlockOperation
 工作原理:
1. NSOperation封装要执行的操作
2. 将创建好的NSOperation对象放NSOperationQueue中 
3. 启动OperationQueue开始新的线程执行队列中的操作
 注意事项:
.1.  使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源, 
    同时运行的线程过多,系统会变慢
.
.2.  使用以下方法可以控制并发的线程数量: 
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
page18image4024.png
NSOperation演练——加载图片 
.1.  不能直接使用NSOperation 
.2.  定义完操作后,将添加到操作队列中,即可启动异步操作,否则操 作任务仍然在主线程中执行 
.3.  使用NSBlockOperation更加简单直接 
.4.  使用setMaxConcurrentOperationCount可以限制并发操作数 
量,降低系统开销  
.5.  使用addDependency可以建立操作之间的依赖关系,设定操作的执行 顺序 
page19image3016.png
 page20image432.png
 
page20image592.png
 
page21image432.png
 
page21image592.png
GCD  GCD是基于C语言的框架
 工作原理:
  让程序平行排队的特定任务,根据可用的处理资源,安排它们在 
   任何可用的处理器上执行任务

  要执行的任务可以是一个函数或者一个block 
  底层是通过线程实现的,不过程序员可以不必关注实现的细节 
  GCD中的FIFO队列称为dispatch queue,可以保证先进来的任务先 得到执行 
  dispatch_notify 可以实现监听一组任务是否完成,完成后得 到通知 
 GCD队列:1. 全局队列:所有添加到主队列中的任务都是并发执行的2. 串行队列:所有添加到串行队列中的任务都是顺序执行的 3. 主队列:所有添加到主队列中的任务都是在主线程中执行的 
page22image4424.png
获取队列的方法 
 全局队列(可能会开启多条线程) dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   串行队列(只可能会开启一条线程) 
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL); 
 主队列  dispatch_get_main_queue(); 
page23image2952.png
GCD任务的执行方式——同步&异步 
 异步操作 
 dispatch_async 在其他线程执行任务,会开启新的线程   异步方法无法确定任务的执行顺序 
 同步操作 dispatch_sync 在当前在当前线程执行任务,不开启新的线程 
 同步操作与队列无关 
 同步方法会依次执行,能够决定任务的执行顺序   更新界面UI时,最好使用同步方法 
page24image2880.png
GCD演练——加载图片 
 GCD的优点: 充分利用多核 所有的多线程代码集中在一起,便于维护  GCD中无需使用@autoreleasepool
 如果要顺序执行,可以使用dispatch_sync同步方法   dispatch_async无法确定任务的执行顺序 
page25image2320.png
单例模型 
 目的: 保证在内存中永远只有类的单个实例
 建立方法:
1. 声明一个静态成员变量,记录唯一实例 
2. 重写allocWithZone方法
allocWithZone方法是对象分配内存空间时,最终会调用的方法, 重写该方法,保证只会分配一个内存空间
3. 建立sharedXXX类方法,便于其他类访问
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html



互斥锁的目的,一次只让一个线程访问资源,从而达到资源的线程安全。

 

iPhone开发中,通常要尽量避免使用互斥锁! 
page26image2832.png

多线程演练——卖票 
 系统预设 共有30张票可以销售(开发时可以少一些,专注实现)  售票工作由两个线程并发进行 没有可出售票据时,线程工作停止 两个线程的执行时间不同,模拟售票人员效率不同 使用一个多行文本框公告售票进度(主线程更新UI)
 线程工作安排 主线程:负责更新UI
 线程1:模拟第1名卖票员  线程2:模拟第2名卖票员  两个线程几乎同时开始卖票
page27image3240.png
page28image552.png
单线程卖票流程图 
page28image1168.png
page29image552.png
多线程卖票示意图 
page29image1168.png
演练准备——更新UI方法,由主线程调用 
// 1. 取出当前的文本 
NSMutableString *str = [NSMutableString stringWithString: [self.textView text]]; 
// 2. 追加文本 [str appendFormat:@"%@\n", text];
// 3. 设置文本 [self.textView setText:str];
// 4. 选中最末位置,实现自动滚动效果 NSRange range = NSMakeRange(str.length - 1, 1);  [self.textView scrollRangeToVisible:range]; 
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
page30image3576.png
卖票演练 
 资源争夺
  仅使用单例模式无法解决资源争夺问题 
  使用互斥锁@synchronized可以保证多个线程不会使用同一代码块,而且比 NSLock具有更好的性能  
  为了保证属性安全,被争夺资源的属性应该设置为原子属性atomic  GCD 
  GCD的多线程更加灵活、方便 
  dispatch_group_notify可以监听一组任务是否完成。这个方法很有用,比如 
    你执行三个下载任务,当三个任务都下载完成后,才通知界面说已经完成

  如果不需要监听一组任务,可以直接使用dispatch_async方法 
 NSOperation
 更新界面时使用[[NSOperationQueue 
mainQueue]addOperationWithBlock:方法   NSThread 
 涉及到线程调度问题,日常开发不建议使用 
page31image4864.png
page32image552.png
三种多线程技术的流程对比 
page32image1168.png
关于iOS多线程使用的建议 
 掌握大纲内容即可,关于多个线程之间的调度问题,待日后熟练后可
以自行学习,目前不建议再继续深入  
 关于多线程必须记住的三个要点
 只能在主线程中更新UI
 共享数据争夺的处理,互斥锁@synchronized(self)和原子属
atomic
 不要使用多种多线程技术去争夺同一个资源 
 使用多线程是为了处理并发操作的。如果有可能,我们不要去做抢资 源的事情 互斥锁的代价相当的昂贵 
page33image2976.png

 

//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html 

https://www.evernote.com/shard/s227/sh/8e2008b3-1d9e-42e6-b193-551e6dbe7440/e39f99e835da159ac641e28679c3c00b

posted @ 2013-12-27 21:37  iTeaTime(技术清谈)  阅读(1814)  评论(0编辑  收藏  举报