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

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

 


多线程 

技术博客http://www.cnblogs.com/ChenYilong/ 新浪微博http://weibo.com/luohanchenyilong
 
多线程的应用 
 耗时操作,例如网络图片视频歌曲书籍等资源下载  游戏中的声音播放
 

 

 

多线程示意图 
 充分发挥多核处理器的优势,并发(同时执行) 执行任务让系统运行的更快、更流畅

 

进程与线程概念 
  一个运行的程序就是一个进程或者叫做一个任务 
  一个进程至少包含一个线程,线程是程序的执行流 
  iOS程序启动时,在创建一个进程的同时, 会开始运行一个线程,该 线程被称为主线程 
  主线程是其他线程最终的父线程,所有界面的显示操作必须在主线程 进行    
  后台线程无法更新UI界面和响应用户点击事件 
  系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中 
的多个线程则共用进程的内存空间 
  每创建一个新的线程,都会消耗一定内存和CPU时间 
  当多个线程对同一个资源出现争夺的时候需要注意线程安全问题 

多线程的优势与难点 
 优势
  充分发挥多核处理器优势,将不同线程任务分配给不同的处 
   理器,真正进入“并行运算”状态

  耗时轮询或者并发需求高等任务分配到其他线程执行, 并由主线程负责统一更新界面会使得应用程序更加流畅,用 户体验更好 
  当硬件处理器的数量增加,程序会运行更快,而无需做任何 调整 
 难点 
  共享资源的“争夺” 
  多线程是为了同步完成多项任务,不是为了提高运行效率, 而是为了通过提高资源使用效率来提高系统的整体性能 

多线程使用注意事项 
 线程使用不是无节制的 iOS中的主线程的堆栈大小是1M
 从第二个线程开始都是512KB
 这些数值不能通过编译器开关或线程API函数更改
 只有主线程有直接修改UI的能力

iOS的三种多线程技术 

1.  NSThread  每个 NSThread 对象对应一个线程,量级较轻(真正的多线
程)
2.  以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去 关心线程的具体使用问题
.1.    NSOperation/NSOperationQueue  面向对象的线程技术 
.2.    GCD —— Grand Central Dispatch (派发)  是基于 C 语言的框架, 可以充分利用多 
     核 ,是苹果推荐使用的多线程技术
.
以上这三种编程方式从上到下,抽象度层次是从低到高的, 抽象度越高 的使用越简单,也是 Apple 最推荐使用的 。但是就目前而言, iOS 的开发者, 需要了解三种多线程技术的基本使用过程。因为很多框架技术分别使用 了不同多线程技术。

三种多线程技术的对比 
  NSThread:
   优点: NSThread  比其他两个轻量级, 使用简单 
   缺点: 需要自己管理线程的生命周期、线程同步、加锁、睡眠以 及唤醒等 。线程同步 对数据的加锁 会有一定的系统开销 
  NSOperation : 
   不需要关心线程管理,数据同步的事情,可以把精力放在自己需 
要执行的操作上 
   NSOperation 是面向对象的    GCD : 
   Grand Central Dispatch 由苹果开发的一个多核编程的解决方案   iOS4.0+ 才能使用,是替代 NSThread ,  NSOperation 的高效和强大 的技术 
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
   GCD 是基于 C 语言的 

演练( 1 ) NSObject 的多线程方法 —— 准备 
.1.    创建一个耗时较长的操作 
.2.    创建一个耗时较短的操作 
.3.    在界面中心放置两个按钮,分别调用 这两个任务 
.4.    使用 [NSThread currentThread] 分别打 印各个任务所在的线程 
.5.    运行观察效果 
   提示: 
   NSLog 是一个相当耗时的操作 在应用 程序正式发布前,一定记住需要对应用 中的 NSLog 方法进行处理 
   无论使用哪一种多线程技术,均可以使 用 [NSThread currentThread] 查看当前任 务所在线程 
 

 

 

NSObject 的多线程方法 —— 后台线程
- ( void )performSelectorInBackground:( SEL )aSelector
withObject:( id )arg 
   通常,由于线程管理相对比较繁琐,而很多耗时的任务又无法知道其准 确的完成时间,因此可以使用 performSelectorInBackground 方法 直接新建一个后台线程,并将选择器指定的任务在后台线程执行,而无 需关心具体的 NSThread 对象   
   提示: 
  performSelectorInBackground 方法本身是在主线程中执行的, 
而选择器指定的方法是在后台线程中进行的 
  使用 performSelectorInBackground 方法调用的任务可以更新 
UI 界面   在大型交互式游戏中,通常使用此方法在后台线程播放音效   

@autoreleasepool  
  内存管理对于多线程非常重要
  Objective-C 可以凭借 @autoreleasepool 使用内存资源,并需要时回 收资源
  每个线程都需要有 @autoreleasepool ,否则可能会出现内存泄漏 

NSObject 的多线程方法 —— 主线程
- ( void )performSelectorOnMainThread:( SEL )aSelector
withObject:( id )arg waitUntilDone:( BOOL )wait; 
  如果要更新 UI 界面,可以在后台线程中调用
performSelectorOnMainThread 方法 
  提示 :尽管使用 performSelectorInBackground 方法调用的任务 可以更新 UI 界面,但是在实际开发中,涉及到 UI 界面的更新操作,还 是要使用 performSelectorOnMainThread 方法,以避免不必要的 麻烦

NSObject 的多线程小结 
  开启后台执行任务的方法
-  ( void )performSelectorInBackground:( SEL )aSelector withObject:( id )arg
  在后台线程中通知主线程执行任务的方法   ( void )performSelectorOnMainThread:( SEL )aSelector
withObject:( id )arg waitUntilDone:( BOOL )wait;    获取线程信息
[ NSThread  currentThread ];    线程休眠
   [ NSThread  sleepForTimeInterval : 1.0f ];
  特点:   使用简单,量级轻   不能控制线程的执行顺序 

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

NSThread 演练 —— 加载图片 
  detachNewThreadSelector
方法会直接启动线程方法 
  initWithTarget 需要调用  start 方法才能够启动线程方 法 
 

 

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

NSOperation 演练 —— 加载图片 
.1.    不能直接使用 NSOperation 
.2.    定义完操作后, 将添加到操作队列中 ,即可启动异步操作,否则操 作任务仍然在主线程中执行 
.3.    使用 NSBlockOperation 更加简单直接 
.4.    使用 setMaxConcurrentOperationCount 可以限制并发操作数 
量,降低系统开销   
.5.    使用 addDependency 可以建立操作之间的依赖关系,设定操作的执行 顺序 

 
 

 

 

GCD    GCD 是基于 C 语言的框架
  工作原理 :
   让程序平行排队的特定任务,根据可用的处理资源, 安排它们在 
   任何可用的处理器上执行任务

   要执行的任务可以是一个函数或者一个 block 
   底层是通过线程实现的,不过程序员可以不必关注实现的细节 
   GCD 中的 FIFO 队列称为 dispatch queue ,可以保证先进来的任务先 得到执行 
   dispatch_notify  可以实现 监听一组任务是否完成 ,完成后得 到通知 
  GCD 队列 : 1.  全局队列: 所有添加到主队列中的任务都是并发执行的 2.  串行队列: 所有添加到串行队列中的任务都是顺序执行的  3.  主队列: 所有添加到主队列中的任务都是在主线程中执行的 

获取队列的方法 
  全局队列(可能会开启多条线程)  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 (); 

GCD 任务的执行方式 —— 同步 & 异步 
  异步操作 
  dispatch_async  在其他线程执行任务,会开启新的线程      异步方法无法确定任务的执行顺序 
  同步操作   dispatch_sync  在当前在当前线程执行任务,不开启新的线程 
  同步操作与队列无关 
  同步方法会依次执行,能够决定任务的执行顺序      更新界面 UI 时,最好使用同步方法 

GCD 演练 —— 加载图片 
  GCD 的优点:   充分利用多核   所有的多线程代码集中在一起,便于维护    GCD 中无需使用 @autoreleasepool
  如果要顺序执行,可以使用 dispatch_sync 同步方法      dispatch_async 无法确定任务的执行顺序 

单例模型 
  目的 :   保证在内存中永远只有类的单个实例
  建立方法 :
1.  声明一个静态成员变量,记录唯一实例 
2.  重写 allocWithZone 方法
allocWithZone 方法是对象分配内存空间时,最终会调用的方法, 重写该方法,保证只会分配一个内存空间
3.  建立 sharedXXX 类方法,便于其他类访问
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html



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

 

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

多线程演练 —— 卖票 
  系统预设   共有 30 张票可以销售( 开发时可以少一些,专注实现)    售票工作由两个线程并发进行   没有可出售票据时,线程工作停止   两个线程的执行时间不同,模拟售票人员效率不同   使用一个多行文本框公告售票进度(主线程更新 UI )
  线程工作安排   主线程:负责更新 UI
  线程 1 :模拟第 1 名卖票员    线程 2 :模拟第 2 名卖票员    两个线程 几乎 同时开始卖票


单线程卖票流程图 


多线程卖票示意图 

演练准备 —— 更新 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

卖票演练 
  资源争夺
   仅使用单例模式无法解决资源争夺问题 
   使用互斥锁 @synchronized 可以保证多个线程不会使用同一代码块,而且比  NSLock 具有更好的性能   
   为了保证属性安全,被争夺资源的属性应该设置为原子属性 atomic    GCD 
   GCD 的多线程更加灵活、方便 
   dispatch_group_notify 可以监听一组任务是否完成 。这个方法很有用,比如 
    你执行三个下载任务,当三个任务都下载完成后,才通知界面说已经完成

   如果不需要监听一组任务,可以直接使用 dispatch_async 方法 
  NSOperation
  更新界面时使用 [[ NSOperationQueue 
mainQueue ] addOperationWithBlock : 方法      NSThread 
  涉及到线程调度问题,日常开发不建议使用 


三种多线程技术的流程对比 

关于 iOS 多线程使用的建议 
  掌握大纲内容即可,关于多个线程之间的调度问题,待日后熟练后可
以自行学习,目前不建议再继续深入   
  关于多线程必须记住的三个要点
  只能在主线程中更新 UI
  共享数据争夺的处理 ,互斥锁 @synchronized ( self ) 和原子属
atomic
  不要使用多种多线程技术去争夺同一个资源 
  使用多线程是为了处理并发操作的。如果有可能,我们不要去做抢资 源的事情 互斥锁的代价相当的昂贵 

 

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

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

你可能感兴趣的:(apple)