iOS线程的同步异步问题

来源:互联网 发布:手机淘宝店铺红包链接 编辑:程序博客网 时间:2024/04/30 05:26

1. 进程、线程、多线程

进程 : 可以简单的理解, 一个应用程序就是一个进程;

线程 : 可以理解为在app中往后运行的通道, 一个进程可以有多个线程;

多线程: 并不是所有的框架都支持多线程, 必须要有多核的cpu支持才行, 单核cpu即使开了多线程运行速度也不会有变化, 开的线程数有几种说法, 其一: 线程数为手机核数的2到3倍, 比如一个双核手机, 开线程数为4到6条; 其二: 根据网络状态, 如果不是wifi状态, 一般开3到4条, 如果是wifi状态,可以开启5到6条;

多线程的目的: 将耗时的操作放在后台, 而不影响主线程与用户的交互;

多线程示意图:


多线程优缺点:

 优点:

1). 能适当提高程序的执行效率,

2). 能适当提高资源的利用率

3). 线程上的任务执行完成后, 线程会自动销毁

缺点:

1). 开启线程需要占用一定的内存空间 ( 默认情况下,每一个线程都占用512Kb ) ( 以前主线程占用1MB空间,现在主线程和子线程都只占用512KB )

2). 如果开启大量的线程,会占用大量的内存空间,CPU会在N个线程之间切换,消耗大量的CPU资源,每个线程被调度的次数会降低,线程的执行效率降低;

3). 程序设计更加复杂,比如线程间的通信、多线程的数据共享

2 . 串行并行,同步异步

串行简单理解就是一个人负责多件事情, 并行是多个人一起负责多个事件;

同步是多个事件按顺序往下执行, 异步是多个事件在多个通道同时执行;

3 . 异步操作和多线程的联系和区别

异步大多数是多线程, 但也不一定是, block方法回调和dispatch的定时函数也是异步操作, 但不一定是多线程, 视当前的代码环境而定;

4. 多编程的技术方案

方案简介语言线程生命周期使用频率pthread
  • 一套通用的多线程API
  • 适用于 Unix / Linux / Windows 等系统
  • 跨平台\可移植
  • 使用难度大
C程序员管理几乎不用NSThread
  • 使用更加面向对象
  • 简单易用,可直接操作线程对象
OC程序员管理偶尔使用GCD
  • 旨在替代NSThread等线程技术
  • 充分利用设备的多核
C自动管理经常使用NSOperation
  • 基于GCD(底层是GCD)
  • 比GCD多了一些更简单实用的功能
  • 使用更加面向对象
OC自动管理经常使用


代码演练:

一、pthread

  1. 知道 C 语言中 void * 与 OC 中的 id 类似
  2. 使用 [NSThread currentThread] 能够在任何多线程技术中,查询当前代码执行所在线程
    1. number == 1,表示主线程
    2. number != 1,表示其他线程
  1. pthread 是 POSIX 多线程开发框架,由于是跨平台的 C 语言框架,在苹果的头文件中并没有详细的注释
  2. 要查阅 pthread 有关资料,可以访问 http://baike.baidu.com
  1. 创建工程 pthread
  2. 导入头文件
    #import <pthread.h>
  3. touchesBegan 创建线程

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {      /*      参数:          1. pthread_t *restrict: 需要 pthread_t(线程标示符) 类型的指针, C语言中的 _t/Ref,都是结构体          2. const pthread_attr_t *restrict: 线程属性          3. void *(*)(void *): 线程调用的函数                  void *      (*)     (void *)                  返回值     函数地址    参数类型          4. void *restrict: 第三个参数的参数      返回值:          0: 表示成功          非0表示失败,一个数字对应一个失败原因      */      // 1> 定义一个 C 字符串  //        char *cName = "zhangsan";      // 2> OC 的字符串      NSString *name = @"lisi";      pthread_t threadId = NULL;      int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(name));      if (result == 0) {          NSLog(@"成功");      } else {          NSLog(@"失败");      }  }
  4. pthread 线程调用函数

    /// 线程调用函数void *demo(void * param) {  NSLog(@"线程 = %@", [NSThread currentThread]);  // 1.打印C语言字符串//    NSLog(@"param = %s", param);  // 2.打印OC字符串//    NSString *name = (__bridge NSString *)(param);  NSLog(@"param = %@", name);  return NULL;}
pthread小结:

  1. 在 C 语言中,没有对象的概念,对象是以结构体的方式来实现的
  2. 通常,在 C 语言框架中,对象类型以 _t/Ref 结尾,而且声明时不需要使用 *
  3. C 语言中的 void * 和 OC 中的 id 是类似的
  4. 内存管理
    • 在 OC 中,如果是 ARC 开发,编译器会在编译时,根据代码结构,自动添加 retain/release/autorelease
    • 但是,ARC 只负责管理 OC 部分的内存管理,而不负责 C 语言 代码的内存管理
    • 开发过程中,如果使用的 C 语言框架出现 retain/create/copy/new 等字样的函数,大多都需要 release,否则会出现内存泄漏
  5. 在混合开发时,如果在 C 和 OC 之间传递数据,需要使用 __bridge 进行桥接,桥接的目的就是为了告诉编译器如何管理内存
    • 桥接的添加可以借助 Xcode 的辅助功能添加
    • MRC 中不需要使用桥接

二、NSThread

NSThread的3中创建线程方式

  1. NSThread 创建线程的三种方式
    1. NSThread 类的 alloc/init 创建线程
    2. NSThread 的 类方法 创建线程
    3. NSObject 分类方法 创建线程
  2. 创建工程 NSThread创建线程
  3. 创建线程第一种方式: 

    1. 使用 NSThread 类的 alloc/init创建线程

       #pragma mark - 创建线程 /// 使用 alloc / init 创建线程 - (void)thread1 {     NSLog(@"thread1 begin %@", [NSThread currentThread]);     // 创建 NSThread 对象     NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(longOperation:) object:@"hello"];     // 启动线程     [thread start];     NSLog(@"thread1 end %@", [NSThread currentThread]); }
    2. 定义 子线程 执行的方法

        #pragma mark - 线程执行方法  - (void)longOperation:(id)param {      NSLog(@"longOperation begin %@", [NSThread currentThread]);      NSLog(@"longOperation end %@", [NSThread currentThread]);  }
    3. 在 touchesBegan:withEvent: 调用
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {      [self thread1];  }
    4. 小结:
      1. 在 OC 中,任何一个线程执行代码都是从上向下顺序执行的
      2. [thread start]; 执行后,会在另外一个线程执行 longOperation: 方法
  4. 使用 NSThread 的 类方法 detachNewThreadSelector 创建线程
      /// 使用 NSThread 类方法  - (void)thread2 {      [NSThread detachNewThreadSelector:@selector(longOperation:) toTarget:self withObject:@"hello2"];  }
    • detachNewThreadSelector 类方法 不需要启动,会自动创建线程并执行 @selector 方法
  5. 使用 NSObject 分类方法 performSelectorInBackground 创建线程
      /// 使用 NSObject 分类方法  - (void)thread3 {      [self performSelectorInBackground:@selector(longOperation:) withObject:@"hello3"];  }
    • 小结:
      1. performSelectorInBackground 是 NSObject 的分类方法
      2. 会自动在后台线程执行 @selector 方法
      3. 没有 thread 字眼,隐式创建并启动线程
      4. 所有 NSObject 都可以使用此方法,在其他线程执行方法
      5. Swift 中不支持
线程的状态:

示意图:


  1. 状态说明
    1. 新建
      • 实例化线程对象
    2. 就绪
      • 向线程对象发送 start 消息,线程对象被加入 可调度线程池 等待 CPU 调度
      • detach 方法和 performSelectorInBackground 方法会直接实例化一个线程对象并加入 可调度线程池
    3. 运行
      • CPU 负责调度可调度线程池中线程的执行
      • 线程执行完成之前,状态可能会在就绪和运行之间来回切换
      • 就绪和运行之间的状态变化由 CPU 负责,程序员不能干预
    4. 阻塞
      • 当满足某个预定条件时,可以使用休眠或锁阻塞线程执行
      • sleepForTimeInterval:休眠指定时长
      • sleepUntilDate:休眠到指定日期
      • @synchronized(self):互斥锁
    5. 死亡
      1. 正常死亡
        • 线程执行完毕
      2. 非正常死亡
        • 当满足某个条件后,在线程内部中止执行
        • 当满足某个条件后,在主线程中止线程对象
      3. 注意:一旦线程停止(死亡)了,就不能再次开启任务

控制线程状态的方法

  1. 启动
    • [thread start]
      • 线程进入就绪状态,线程对象被加入 可调度线程池 等待 CPU 调度, 当线程执行完毕后自动进入死亡状态
  2. 休眠
    • 方法执行过程中,符合某一条件时,可以利用 sleep 方法让线程进入 阻塞 状态
      • sleepForTimeInterval 从现在起睡多少秒
      • sleepUntilDate 从现在起睡到指定的日期
  3. 死亡
    • [NSThread exit]
      • 一旦强行终止线程,后续的所有代码都不会被执行
      • 注意:在终止线程之前,应该注意释放之前分配的对象!
  4. 取消
    • [_thread cancel]
      • 并不会直接取消线程
      • 只是给线程对象添加 isCancelled 标记
      • 需要在线程内部的关键代码位置,增加判断,决定是否取消当前线程
三、GCD

GCD的方法很多,用法也很多,这里只列举一些常用的方法。常用的方法包括:

  • 同步、非阻塞执行
  • 异步非阻塞执行
  • 一次性执行
  • 延迟执行
  • 线程队列串行执行
  • 线程队列控制(屏障,同步等待,线程暂停和恢复,线程信号量控制等)
[objc] view plain copy
  1. <span style="font-family:Open Sans, Clear Sans, Helvetica Neue, Helvetica, Arial, sans-serif;color:#333333;">/* 
  2.  *使用GCD 的多线程 
  3.  *优点:有很多串行并线队列多线程,block实现线程方法,高级,好用,方法多。 
  4.  *缺点:在很多不需要高级控制线程的场景可以不用使用GCD 
  5.  */  
  6. -(void)GCDFunction{  
  7.   
  8.     NSLog(@"GCDFunction start");  
  9.   
  10.     //获取一个队列  
  11.     dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  12.   
  13.     //dispatch_async:异步方式执行方法(最常用)  
  14.     //    dispatch_async(defaultQueue, ^{  
  15.     //        [self function1];  
  16.     //    });  
  17.   
  18.     //dispatch_sync:同步方式使用场景,比较少用,一般与异步方式进行调用  
  19.     //    dispatch_async(defaultQueue, ^{  
  20.     //       NSMutableArray *array = [self GCD_sync_Function];  
  21.     //       dispatch_async(dispatch_get_main_queue(), ^{  
  22.     //           //利用获取的arry在主线程中更新UI  
  23.     //  
  24.     //       });  
  25.     //    });  
  26.   
  27.     //dispatch_once:一次性执行,常常用户单例模式.这种单例模式更安全  
  28.     //    static dispatch_once_t onceToken;  
  29.     //    dispatch_once(&onceToken, ^{  
  30.     //        // code to be executed once  
  31.     //        NSLog(@"dispatch_once");  
  32.     //    });  
  33.   
  34.     //dispatch_after 延迟异步执行  
  35.     //    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC);  
  36.     //    dispatch_after(popTime, defaultQueue, ^{  
  37.     //        NSLog(@"dispatch_after");  
  38.     //    });  
  39.   
  40.   
  41.     //dispatch_group_async 组线程可以实现线程之间的串联和并联操作  
  42.     //    dispatch_group_t group = dispatch_group_create();  
  43.     //    NSDate *now = [NSDate date];  
  44.     //    //做第一件事 2秒  
  45.     //    dispatch_group_async(group, defaultQueue, ^{  
  46.     //        [NSThread sleepForTimeInterval:2];  
  47.     //         NSLog(@"work 1 done");  
  48.     //    });  
  49.     //    //做第二件事 5秒  
  50.     //    dispatch_group_async(group, defaultQueue, ^{  
  51.     //        [NSThread sleepForTimeInterval:5];  
  52.     //        NSLog(@"work 2 done");  
  53.     //    });  
  54.     //  
  55.     //    //两件事都完成后会进入方法进行通知  
  56.     //    dispatch_group_notify(group, defaultQueue, ^{  
  57.     //        NSLog(@"dispatch_group_notify");  
  58.     //        NSLog(@"%f",[[NSDate date]timeIntervalSinceDate:now]);//总共用时5秒,因为2个线程同时进行  
  59.     //    });  
  60.   
  61.   
  62.     //dispatch_barrier_async :作用是在并行队列中,等待前面的队列执行完成后在继续往下执行  
  63.     //    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);  
  64.     //    dispatch_async(concurrentQueue, ^{  
  65.     //        [NSThread sleepForTimeInterval:2];  
  66.     //        NSLog(@"work 1 done");  
  67.     //    });  
  68.     //    dispatch_async(concurrentQueue, ^{  
  69.     //        [NSThread sleepForTimeInterval:2];  
  70.     //        NSLog(@"work 2 done");  
  71.     //    });  
  72.     //    //等待前面的线程完成后执行  
  73.     //    dispatch_barrier_async(concurrentQueue, ^{  
  74.     //         NSLog(@"dispatch_barrier_async");  
  75.     //    });  
  76.     //  
  77.     //    dispatch_async(concurrentQueue, ^{  
  78.     //        [NSThread sleepForTimeInterval:3];  
  79.     //        NSLog(@"work 3 done");  
  80.     //    });  
  81.   
  82.   
  83.   
  84.     //dispatch_semaphore 信号量的使用,串行异步操作  
  85.     //    dispatch_semaphore_create   创建一个semaphore  
  86.     //   dispatch_semaphore_signal   发送一个信号  
  87.     //   dispatch_semaphore_wait    等待信号  
  88.   
  89.   
  90.     /*应用场景1:马路有2股道,3辆车通过 ,每辆车通过需要2秒 
  91.      *条件分解: 
  92.         马路有2股道 <=>  dispatch_semaphore_create(2) //创建两个信号 
  93.         三楼车通过 <=> dispatch_async(defaultQueue, ^{ } 执行三次 
  94.         车通过需要2秒 <=>  [NSThread sleepForTimeInterval:2];//线程暂停两秒 
  95.      */  
  96.   
  97.     dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);  
  98.   
  99.         dispatch_async(defaultQueue, ^{  
  100.             dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);  
  101.             [NSThread sleepForTimeInterval:2];  
  102.             NSLog(@"carA pass the road");  
  103.             dispatch_semaphore_signal(semaphore);  
  104.         });  
  105.         dispatch_async(defaultQueue, ^{  
  106.             dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);  
  107.             [NSThread sleepForTimeInterval:2];  
  108.             NSLog(@"carB pass the road");  
  109.             dispatch_semaphore_signal(semaphore);  
  110.         });  
  111.         dispatch_async(defaultQueue, ^{  
  112.             dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);  
  113.             [NSThread sleepForTimeInterval:2];  
  114.             NSLog(@"carC pass the road");  
  115.             dispatch_semaphore_signal(semaphore);  
  116.         });  
  117.   
  118.   
  119.   
  120.     //应用场景2 :原子性保护,保证同时只有一个线程进入操作  
  121.     //    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);  
  122.     //    for(int i=0 ;i< 10000 ;i++){  
  123.     //        dispatch_async(defaultQueue, ^{  
  124.     //            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);  
  125.     //            NSLog(@"i:%d",i);  
  126.     //            dispatch_semaphore_signal(semaphore);  
  127.     //        });  
  128.     //    }  
  129.   
  130.   
  131.     NSLog(@"GCDFunction end");  
  132. }</span>  

四、NSOperation

NSOperation需要在NSOperationQueue中使用,通过queue可以实现先进先出的队列任务,可以添加或取消任务,NSOperation有2个重要的子类,分别是:NSInvocationOperation,NSBlockOperation,分别表示调用一个方法或调用一个block的任务。 NSOperation是比GCD更高层次的api,相同的线程操作如果能用NSOperation操作就尽量用,不能实现的线程操作才使用GCD.相比GCD,NSOperation还有个好处,就是任务可以被取消,而GCD不可以。

[objc] view plain copy
  1. <span style="font-family:Open Sans, Clear Sans, Helvetica Neue, Helvetica, Arial, sans-serif;font-size:14px;color:#333333;">-(void)NSOperationFunction{  
  2.     NSOperationQueue *queue = [[NSOperationQueue alloc]init];  
  3.     //设置队列最大同时进行的任务数量,1为串行队列  
  4.     [queue setMaxConcurrentOperationCount:1];  
  5.     //添加一个block任务  
  6.     [queue addOperationWithBlock:^{  
  7.        sleep(2);  
  8.         NSLog(@"block task 1");  
  9.     }];  
  10.     [queue addOperationWithBlock:^{  
  11.         sleep(2);  
  12.         NSLog(@"block task 2");  
  13.     }];  
  14.     //显示添加一个block任务  
  15.     NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{  
  16.         sleep(2);  
  17.         NSLog(@"block task 3");  
  18.     }];  
  19.     //设置任务优先级  
  20.     //说明:优先级高的任务,调用的几率会更大,但不表示一定先调用  
  21.     [block1 setQueuePriority:NSOperationQueuePriorityHigh];  
  22.     [queue addOperation:block1];  
  23.   
  24.     NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{  
  25.         sleep(2);  
  26.         NSLog(@"block task 4,任务3依赖4");  
  27.     }];  
  28.     [queue addOperation:block2];  
  29.     //任务3依赖4  
  30.     [block1 addDependency:block2];  
  31.     //设置任务完成的回调  
  32.     [block2 setCompletionBlock:^{  
  33.          NSLog(@"block task 4 comlpete");  
  34.     }];  
  35.   
  36.     //设置block1完成后才会继续往下走  
  37.     [block1 waitUntilFinished];  
  38.      NSLog(@"block task 3 is waitUntilFinished!");  
  39.   
  40.     //初始化一个子任务  
  41.     NSInvocationOperation *oper1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(function1) object:nil];  
  42.     [queue addOperation:oper1];  
  43.   
  44.     [queue waitUntilAllOperationsAreFinished];  
  45.     NSLog(@"queue comlpeted");  
  46.   
  47.     //    取消全部操作  
  48.     //    [queue cancelAllOperations];  
  49.     //    暂停操作/恢复操作/是否暂定状态  
  50.     //    [queue setSuspended:YES];[queue setSuspended:NO];[queue isSuspended];  
  51.   
  52.   
  53.     //操作优先级  
  54.   
  55.   
  56.   
  57.     //      [queue waitUntilAllOperationsAreFinished];</span>  

[objc] view plain copy
  1. 2016-02-04 15:11:54.283 ThreadAndAsynchronization[28948:3783683] block task 1  
  2. 2016-02-04 15:11:56.358 ThreadAndAsynchronization[28948:3783684] block task 2  
  3. 2016-02-04 15:11:58.430 ThreadAndAsynchronization[28948:3783683] block task 4,任务3依赖4  
  4. 2016-02-04 15:11:58.430 ThreadAndAsynchronization[28948:3783694] block task 4 comlpete  
  5. 2016-02-04 15:12:00.504 ThreadAndAsynchronization[28948:3783683] block task 3  
  6. 2016-02-04 15:12:00.504 ThreadAndAsynchronization[28948:3783527] block task 4 is waitUntilFinished!  
  7. 2016-02-04 15:12:02.573 ThreadAndAsynchronization[28948:3783694] function1 done  
  8. 2016-02-04 15:12:02.573 ThreadAndAsynchronization[28948:3783527] queue comlpeted  

有2个值得注意的地方,第一个是mainQueue,第二个是maxConcurrentOperationCount。

mainQueue是通过 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; 获取到,它代表主队列,也就是UI队列,所以用到mainQueue队列的时候一般用于更新ui界面,且特别注意在这个队列中执行的方法,要考虑到会不会阻塞进程。

maxConcurrentOperationCount:最多有多少个队列可以同时执行,默认是5,当设置为1是,队列是一个串行队列,设置>1时,队列是一个并行队列。但是在主队列上设置同时执行的任务是没有效果的!如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能内存多久开多一点,内存少就开少一点。

  • 取消队列的所有操作 [queue cancelAllOperations];
  • 暂停队列恢复
[objc] view plain copy
  1. //    [queue setSuspended:YES];  
  2. //    [queue setSuspended:NO];  
  3. //    [queue isSuspended];  

操作依赖

[objc] view plain copy
  1. //block1依赖block2  
  2.     [block1 addDependency:block2];  


操作完成后的回调

[objc] view plain copy
  1. //设置任务完成的回调  
  2.   [block2 setCompletionBlock:^{  
  3.        NSLog(@"block task 4 comlpete");  
  4.   }];  

0 0
原创粉丝点击