Multithreading in iOS
进程和线程
进程是指正在运行的一个应用程序实例。每个进程都是独立的,运行在受保护的内存空间里面。
一个进程想执行任务,必须要至少有一个线程。线程是进程的基本执行单元。
多线程
一个进程里开启多条线程,每条线程并发执行不同的任务。多线程可以提高程序的运行效率。
原理
同一时间,CPU 只能处理一条线程,多线程是 CPU 不停地各个线程间切换造成的假象。
优缺点
优点:
- 提高程序的执行效率
- 提高资源的利用率(CPU,内存利用率)
缺点:
- 开启线程需要占用内存空间,开启大量线程会降低程序的性能
- 线程越多,CPU 在线程切换的开销越大
- 程序设计更加复杂(线程间通信)
多线程在 iOS 开发中的应用
主线程
程序运行后,默认会开启一条线程(UI 线程)。
作用是,显示、刷新 UI 界面,处理 UI 事件。
不要将比较耗时的操作放到主线程中。
多线程实现方案
使用 NSThread
- 一个 NSThread 对象就代表一条线程
- 线程函数结束以后就代表线程生命周期终结,不能再次启动线程,而是得创建新的线程
// 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadFunction) object:nil];
// 启动线程
[thread start];
GCD
GCD 的全称是 Grand Central Dispatch,对于这种专业词汇就不要翻译成「大神经中枢派发」了,就了解它是一种多线程技术就好了。 GCD 有两个关键概念: GCD 的使用就是创建任务,接着把任务添加到队列就好了。 #### 任务 执行的操作
// 使用同步的方式执行,在当前线程执行
void dispatch_sync ( dispatch_queue_t queue, dispatch_block_t block );
// 异步
void dispatch_async ( dispatch_queue_t queue, dispatch_block_t block );
队列
用来存放任务 - 串行队列:这个队列的所有任务都是按顺序进行的 - 并发队列:所有任务都是同时进行的,线程的创建交由操作系统进行
- 获得全局并发队列:
dispatch_queue_t dispatch_get_global_queue ( long identifier, unsigned long flags );
GCD 中的几个函数
// 延时执行
dispatch_after
// 只执行一次
dispatch_once
GCD 中的队列组
队列组是把任务都放进一个组里,当组里所有的任务都被处理完毕以后,会通知
// 创建组
dispatch_group_t group = dispatch_group_create();
// 获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 进行耗时操作
dispatch_group_async(group, queue, ^{
});
// 进行耗时操作
dispatch_group_async(group, queue, ^{
});
// 前两个任务都结束以后,会调用这个 block
dispatch_group_notify(group, queue, ^{
});
多线程的安全隐患
多个线程访问同一块资源(对象,变量,文件等)。 ### 互斥锁
@synchronized(self) {
// 加锁区域
// 小括号里面放的是锁的对象,通常传 self 就可以(代表同一把锁)
}
优点: - 能解决线程安全问题 缺点: - 消耗大量 CPU 资源 ## 线程间的通信 在一个线程执行下载内容,但是需要把下载好的东西更新到 UI 上,这时候需要在线程之间传递东西。 不可以在子线程执行更新 UI 的操作,因为主线程也会对 UI 操作,这样会发生问题的。
// 回到主线程操作,是 NSObject 的方法,任意控件都可以执行这种操作
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
// 使用 GCD,在子线程的 BLOCK 中,再调用 dispatch_async 回到主线程更新 UI