iOS多线程

302 阅读4分钟

多线程简介

1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务,提高程序的执行效率。

进程

  • 进程是指在系统中正在运行的一个应用程序
  • 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内

线程

  • 一个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程
  • 一个进程(程序)的所有任务都在线程中执行

线程串行

  • 一个一个的按顺序执行

进程和线程的比较

  1. 线程是CPU调用(执行任务)的最小单位
  2. 进程是CPU分配资源和调度的单位
  3. 一个程序可以对应多个进程,一个进程中可以有多个线程,但至少要有一个线程
  4. 同一个进程内的线程共享进程的资源

多线程原理

  • 同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
  • 多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
  • 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象

iOS中多线程的实现方案

多线程实现方案

线程的状态和安全

线程状态

线程状态

// 控制线程状态
// 启动线程 - 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
- (void)start;

// 阻塞(暂停)线程 - 进入阻塞状态
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

// 强制停止线程 - 进入死亡状态
+ (void)exit;

线程安全

安全隐患解决

互斥锁 @synchronized(锁对象) { // 需要锁定的代码 }

原子和非原子属性

  • atomic:原子属性(默认)
  • nonatomic:非原子属性

NSThread

// 1.1 创建、启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];

// 1.2 其他创建线程方式(优点:简单快捷;  缺点:无法对线程进行更详细的设置 )
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; // 创建线程后自动启动线程
[self performSelectorInBackground:@selector(run) withObject:nil]; // 隐式创建并启动线程

// 生命周期:当任务执行完毕之后会被释放掉

线程间通信

[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDown:NO]; // 方式一
[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDown:NO]; // 方式二
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDown:NO]; // 方式三

NSOperation

NSOperationNSOperationQueue实现多线程的具体步骤

  1. 先将需要执行的操作封装到一个NSOperation对象中
  2. 然后将NSOperation对象添加到NSOperationQueue
  3. 系统会自动将NSOperationQueue中的NSOperation取出来
  4. 将取出的NSOperation封装的操作放到一条新线程执行

NSOperation 是个抽象类,不具备封装操作的能力,必须使用其子类:NSInvocationOperationNSBlockOperation

NSOperationQueue的基本使用

/**
 NSOperation: 
 取主队列:[NSOperationQueue mainQueue]; 和GCD中的主队列一样
 非主队列:[[NSOperationQueue alloc] init]; 非常特殊(同时具备并发和串行的功能)默认是并发
 */
NSOperationQueue *queue = [[NSOperationQueue alloc] init];  // 默认并发队列
queue.maxConcurrentOperationCount = 5; // 设置最大并发数量(等于1时则串行执行任务 != 只开一条线程)
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    // 执行任务
}];
[queue addOperation:op1]; // 会自动执行start

[queue setSuspended:YES];   // 暂停 (不能暂停当前正在执行状态的任务)
[queue setSuspended:NO];    // 继续执行
[queue cancelAllOperations];// 取消

// 操作依赖,不能循环依赖,可以跨队列依赖  例:
[op1 addDependency:op4];
[op4 addDependency:op2];
[op2 addDependency:op3];
// 上面依赖执行顺序为: op3 -> op2 -> op4 -> op1 

// 操作监听
op3.completionBlock = ^{
    // op3执行完成后执行该监听回调,这里面不跟op3中的任务不一定是同一线程中
}

GCD

GCD基本知识

  1. 两个核心概念 - 任务和队列
  2. 同步函数(不具备开启新线程能力)和异步函数(具备开启新线程能力)
  3. 并发队列串行队列

GCD基本使用

各队列执行效果

// 创建队列 - DISPATCH_QUEUE_SERIAL:串行 / DISPATCH_QUEUE_CONCURRENT:并发
dispatch_queue_t queue = dispatch_queue_create("com.ltd.download", DISPATCH_QUEUE_SERIAL);
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();

GCD其他函数

/**
 1.延迟执行
 第一个参数:DISPATCH_TIME_NOW 从现在开始计算时间
 第二个参数:延迟的时间 2.0 GCD时间单位:纳秒
 第三个参数:队列
 */
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 执行任务
});

// 2.执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 执行任务
});

// 3.栅栏函数(控制任务的执行顺序)(注意:栅栏函数不能使用全局并发队列)
dispatch_barrier_async(queue, ^{
    // 执行任务
});

/**
 4.快速迭代 (开子线程和主线程一起完成遍历任务,任务的执行是并发的)
 第一个参数:遍历的次数
 第二个参数:队列(只能是并发队列,不然没啥作用或死锁)
 第三个参数:index 索引
 */
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
    // 执行任务
});

// 5.队列组
dispatch_group_t group = dispatch_group_create();   // 创建队列组
dispatch_group_async(group, queue, dispatch_block_t block); // 任务
dispatch_group_notify(group, queue, dispatch_block_t block); // 队列组中的任务执行完毕之后,执行该函数
dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 死等