iOS 多线程学习笔记

进程和线程

进程是指正在运行的一个应用程序实例。每个进程都是独立的,运行在受保护的内存空间里面。

一个进程想执行任务,必须要至少有一个线程。线程是进程的基本执行单元。

多线程

一个进程里开启多条线程,每条线程并发执行不同的任务。多线程可以提高程序的运行效率。

原理

同一时间,CPU 只能处理一条线程,多线程是 CPU 不停地各个线程间切换造成的假象。

优缺点

优点:

  • 提高程序的执行效率
  • 提高资源的利用率(CPU,内存利用率)

缺点:

  • 开启线程需要占用内存空间,开启大量线程会降低程序的性能

  • 线程越多,CPU 在线程切换的开销越大

  • 程序设计更加复杂(线程间通信)

多线程在 iOS 开发中的应用

主线程

程序运行后,默认会开启一条线程(UI 线程)。

作用是,显示、刷新 UI 界面,处理 UI 事件。

不要将比较耗时的操作放到主线程中。

多线程实现方案

name 简介 语言 生命周期
pthread 基于 UNIX,跨平台 C 程序员管理
NSThread 可直接操作线程对象 Objective-C 程序员管理
GCD 替代 NSThread,充分利用设备的核心 C 系统管理
NSOperation 基于 GCD,更加面向对象 Objective-C 系统管理

使用 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 中的队列组
队列组是把任务都放进一个组里,当组里所有的任务都被处理完毕以后,会通知

```objc
// 创建组
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