• 多线程


    多线程 

    技术博客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的高效和强大 的技术 
    - 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)targetwithObject:(id)argument; 
    2. - (id)initWithTarget:(id)targetselector: (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; 


    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类方法,便于其他类访问



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

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


    多线程演练--卖票 

     系统预设- 共有30张票可以销售(开发时可以少一些,专注实现) - 售票工作由两个线程并发进行- 没有可出售票据时,线程工作停止- 两个线程的执行时间不同,模拟售票人员效率不同- 使用一个多行文本框公告售票进度(主线程更新UI)

     线程工作安排- 主线程:负责更新UI

    - 线程1:模拟第1名卖票员 - 线程2:模拟第2名卖票员 - 两个线程几乎同时开始卖票


    单线程卖票流程图 


    多线程卖票示意图 


    演练准备--更新UI方法,由主线程调用

    // 1. 取出当前的文本 
    NSMutableString *str = [NSMutableStringstringWithString: [self.textView text]]; 
    // 2. 追加文本 [str appendFormat:@"%@ ", text];
    // 3. 设置文本 [self.textView setText:str];
    // 4. 选中最末位置,实现自动滚动效果 NSRange range =NSMakeRange(str.length - 1, 1); [self.textView scrollRangeToVisible:range]; 


    卖票演练 
     资源争夺
    - 仅使用单例模式无法解决资源争夺问题 
    - 使用互斥锁@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/(点击RSS订阅)
    本文版权归作者和博客园共有,欢迎转载,
    但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    编辑推荐
    mybatis
    学习网址记录
    关于详情页的具体制作(四)
    关于详情页的具体制作(三)
    关于详情页的具体制作(二)
    关于详情页的具体制作(一)
    关于事件循环的一些总结
    vue生命周期的一些总结
    对于home主页的切换处理
  • 原文地址:https://www.cnblogs.com/ChenYilong/p/3555234.html
Copyright © 2020-2023  润新知