iOS多线程总结

思维导图

一、基本概念

  1. 进程:一个独立运行的程序,关于某个数据集合的一次运行活动。在iOS中可以理解为一个运行的APP。在一个进程中可以包含多个线程。
  2. 线程:程序执行的最小单元,线程是进程中的一个实体。
    • 同步:在当前线程中按照先后顺序依次执行任务,不开启新的线程。同步可以看成是单线程操作,会造成线程的阻塞。
    • 异步:在当前线程中开启多个新线程执行,不一定按照先后顺序。异步操作一定是开启多个线程。
  3. 队列:装载线程任务的队形结构,队列可分为并发队列和串行队列。
    • 并发:多个线程可以同时一起执行。
    • 串行:线程执行只能按照一定的顺序,依次逐一先后有序的执行。

二、iOS中几种多线程对比

1. pthread

  • 一套C语言实现的通用的多线程API
  • 适用于Unix/Linux/Windows等操作系统
  • 可移植,使用难度略大
  • 生命周期由程序员手动管理

2. NSThread

  • 由OC实现的面对对象的多线程
  • 操作较为简单,可以直接操作多线程对象
  • 生命周期由程序员手动管理

3. GCD

  • C实现的替代NSThread的多线程技术
  • 操作简单,易用
  • 系统自动管理生命周期

4. NSOperation

  • 基于GCD(底层是GCD实现),OC实现的多线程对象
  • 比GCD多了一些实用功能,使用更加面向对象
  • 系统自动管理生命周期

三、使用方法

1. pthread

iOS中很少直接调用pthread_create来创建线程,所以这里引用他人的一篇文章,里面关于线程的创建、管理以及互斥锁的使用做了详细的介绍。这里是连接

2. NSThread

  • NSThread创建线程
// 方法1 创建之后需要手动启动
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething1:) object:@"NSThread1"];
[thread1 start];

// 方法2 创建之后自动启动
[NSThread detachNewThreadSelector:@selector(doSomething2:) toTarget:self withObject:@"NSThread2"];

[NSThread detachNewThreadWithBlock:^{
        // do something
}];

// 方法3 隐式创建,直接启动
[self performSelectorInBackground:@selector(doSomething3:) withObject:@"NSThread3"];

  • NSThread的类方法
/* 1. 获取当前线程 */
   [NSThread currentThread];

   int num = [NSThread currentThread];
   //num = 1,表示在主线程,否则是在子线程。
/* 2. 阻塞线程    */
   // 线程进入休眠,2s后开启
   [NSThread sleepForTimeInterval:2];
   // 线程休眠到某个时间开启
   [NSThread sleepUntilDate:[NSDate date]];
/* 3. 其他方法*/
   //退出线程
   [NSThread exit];
   //判断当前线程是否为主线程
   [NSThread isMainThread];
   //判断当前线程是否是多线程
   [NSThread isMultiThreaded];
   //主线程的对象
   NSThread *mainThread = [NSThread mainThread];
  • NSThread的属性

//线程是否在执行
thread.isExecuting;
//线程是否被取消
thread.isCancelled;
//线程是否完成
thread.isFinished;
//是否是主线程
thread.isMainThread;
//线程的优先级,取值范围0.0到1.0,默认优先级0.5,1.0表示最高优先级,优先级高,CPU调度的频率高
thread.threadPriority;

3. GCD

  • GCD的使用步骤

    1. 创建队列(串行队列或并行队)
    2. 将任务添加到队列中,系统会根据任务类型执行任务(同步执行或者异步执行)
  • GCD的具体使用方法

    1. 创建队列的方法

      • 可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并行队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并行队列。
      // 串行队列创建方法
      dispatch_queue_t queue = dispatch_aueue_create("testQueue", DISPATCH_QUEUE_SERIAL);
      // 并行队列的创建方法
      dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
      • 对于并行队列,还可以使用dispatch_get_global_queue来创建全局并行队列。GCD默认提供了全局的并行队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。
    2. 添加任务的方法

      // 同步执行任务创建方法
      dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);    // 这里放任务代码
      });
      // 异步执行任务创建方法
      dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);    // 这里放任务代码
      });
  • 队列和任务的不同的组合方式



















    并行队列串行队列主队列
    同步(sync) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务
    异步(async) 有开启新线程,并行执行任务 有开启新线程(1条),串行执行任务 没有开启新线程,串行执行任务

4.NSOperation

  • NSOperation实现步骤
    1. 实例化NSOperation的子类,绑定执行的操作
    2. 创建NSOperationQueue队列,将NSOperation实例添加到队列中
    3. 系统将自动检测NSOperation中的NSOperation对象并执行
  • NSOperation具体使用方法

    NSOperation是抽象类,在实际运用的时候要用到它子类:NSInvocationOperation,NSBlockOperation,或者自定义子类继承NSOperation,实现内部相应的方法(这里不做介绍,自定义类的详细介绍点这里

    • NSInvocationOperation单独使用
    
    // 创建NSInvocationOperation
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
    // 开始执行操作
    [invocationOperation start];
    // 注意:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
    
    • NSBlockOperation单独使用
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        // 在主线程
        NSLog(@"下载1------%@", [NSThread currentThread]);
    }];
    
    // 添加额外的任务(在子线程执行)
    [operation addExecutionBlock:^{
        NSLog(@"下载2------%@", [NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"下载3------%@", [NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"下载4------%@", [NSThread currentThread]);
    }];
    
    [operation start];
    
    // 注意:只要NSBlockOperation封装的操作数 >1,就会异步执行操作
    • NSInvocationOperation/NSBlockOperation配合NSOperationQueue使用
    NSOperationQueue *qu = [[NSOperationQueue alloc] init];  
    
    NSBlockOperation * bkOp1 = [NSBlockOperation blockOperationWithBlock:^{  
        sleep(10);  
        NSLog(@"bkOp1.....handle.....on thread num%@",[NSThread currentThread]);  
    }];  
    [bkOp1 setCompletionBlock:^{  
        NSLog(@"bkOp1........OK !!");  
    }];  
    
    
    NSBlockOperation * bkOp2 = [NSBlockOperation blockOperationWithBlock:^{  
        sleep(2);  
        NSLog(@"bkOp2.....handle.....on thread num%@",[NSThread currentThread]);  
    }];  
    [bkOp2 setCompletionBlock:^{  
        NSLog(@"bkOp2........OK !!");  
    }];  
    
    NSBlockOperation * bkOp3 = [NSBlockOperation blockOperationWithBlock:^{  
        sleep(1);  
        NSLog(@"bkOp3.....handle.....on thread num%@",[NSThread currentThread]);  
    }];  
    [bkOp3 setCompletionBlock:^{  
        NSLog(@"bkOp3........OK !!");  
    }];  
    
    NSBlockOperation * bkOp4 = [NSBlockOperation blockOperationWithBlock:^{  
        sleep(10);  
        NSLog(@"bkOp4.....handle.....on thread num%@",[NSThread currentThread]);  
    }];  
    [bkOp4 setCompletionBlock:^{  
        NSLog(@"bkOp4........OK !!");  
    }];  
    
    NSBlockOperation * bkOp5 = [NSBlockOperation blockOperationWithBlock:^{  
        sleep(5);  
        NSLog(@"bkOp5.....handle.....on thread num%@",[NSThread currentThread]);  
    }];  
    [bkOp5 setQueuePriority:NSOperationQueuePriorityHigh];  
    [bkOp5 setCompletionBlock:^{  
        NSLog(@"bkOp5........OK !!");  
    }];  
    
    NSInvocationOperation *invoOp6 = [[NSInvocationOperation alloc] initWithTarget:(id)self selector:@selector(handleInvoOp) object:nil];  
    [invoOp6 setCompletionBlock:^{  
        NSLog(@"invoOp6........OK !!");  
    }];  
    [invoOp6 setQueuePriority:NSOperationQueuePriorityHigh];  
    
    [qu setMaxConcurrentOperationCount:2];  
    [qu addOperation:bkOp3];  
    [qu addOperation:bkOp2];  
    [qu addOperation:bkOp1];  
    [qu addOperation:bkOp4];  
    [qu addOperation:bkOp5];  
    [qu addOperation:invoOp6];
    
    //注意:NSOperation可以调用start方法来执行任务,但默认是同步执行的,如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
    
  • NSOperation之间的操作依赖

    • NSOperation之间可以设置依赖来保证执行顺序,比如一定要让操作A执行完后,才能执行操作B,可以这么写
    [operationB addDependency:operationA];// 操作B依赖于操作A
    • 举例说明,下载->解压->更新UI
    /** 
     举例场景: 
     1. 下载一个小说的压缩包 
     2. 解压缩,删除压缩包 
     3. 更新UI 
     */  
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{  
        NSLog(@"下载一个小说的压缩包,%@",[NSThread currentThread]);  
    }];  
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{  
        NSLog(@"解压缩,删除压缩包,%@",[NSThread currentThread]);  
    }];  
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{  
        NSLog(@"更新UI,%@",[NSThread currentThread]);  
    }];  
    // 如果直接把任务添加到队里,会异步执行,使顺序错乱  
    
    // 因此需要指定任务之间的依赖关系--------依赖关系可以跨队列(可以在子线程下载完,到主线程更新UI)  
    [op2 addDependency:op1];  // op2 依赖于op1  就是执行op2之前必须先执行op1  
    [op3 addDependency:op2];  // op3 依赖于op2  就是执行op3之前必须先执行op2  
    
    /** 
     千万注意:不要造成相互依赖即依赖循环,会造成死锁 
     */  
    //    [op1 addDependency:op3];  
    
    
    // waitUntilFinished 类似于GCD中调度组的通知  
    // NO表示不等待当前的队列执行完毕,就执行下面的代码,打印 NSLog(@"任务完成");  
    // YES 表示必须等队列内的任务全部执行完毕才执行下面的代码  
    [ self.opQueue addOperations:@[op1, op2] waitUntilFinished:YES];  
    
    // 在主线程更新UI  
    [[NSOperationQueue mainQueue] addOperation:op3];  
    
    NSLog(@"任务完成");
  • NSOperation线程间通讯

    
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
    
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
    
        // 生成图片
        UIImage *image = [UIImage imageWithData:data];
    
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    

四、参考文章

感谢分享

https://www.jianshu.com/p/6e6f4e005a0b

https://www.jianshu.com/p/2d57c72016c6

http://www.cocoachina.com/ios/20170707/19769.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值