iOS开发:多线程之理解任务和队列
Posted wuwuFQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发:多线程之理解任务和队列相关的知识,希望对你有一定的参考价值。
本文着重点是任务和队列,线程是什么?多线程有什么用?怎么正确使用任务和队列?案例使用GCD讲解。
进程和线程
- 进程:它是操作系统分配资源的基本单元,是在系统中正在运行的一个程序,可以理解为手机上的一个app;进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内,拥有独立运行的全部资源。
- 线程:程序执行流的最小单元,线程是进程中的一个实体;一个进程要想执行任务,必须至少有一条线程,应用程序启动的时候,系统会默认开启一条线程,也就是主线程。
- 进程和线程的关系:线程是进程的执行单元,进程的所有任务都在线程中执行;线程是CPU分配资源和调度的最小单位;一个程序可以对应多个进程(多进程),一个进程可以对应多个线程,但至少有一个线程;同一个进程内的线程共享进程资源
线程是进程的执行单元,一个进程至少有一个线程。
多进程和多线程
- 多进程:进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。在同一个时间里,同 一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多进程。
- 多线程:同一时间,CPU只能处理1条线程,只有1条线程在执行。多线程并发执行,其实是CPU快速地在多条线程之间调度(切换)。如果 CPU 调度线程的时间足够快,就造成了多线程并发执行的假象。
多线程的优点: 能适当提高程序的执行效率 能适当提高资源利用率(CPU、内存利用率);
多线程的缺点: 开启线程需要占用一定的内存空间(默认情况下,主线程占用 1M,子线程占用 512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能,CPU会在N多线程之间调度,消耗大量的CPU资源,每条线程被调度执行的频次会降低。
任务和队列
- 任务:就是执行操作的意思,也就是在线程中执行的业务代码。在GCD中是放在block中的。执行任务有两种方式:同步执行(sync)和异步执行(async)。
- 同步(Sync):只能在当前线程中执行任务,不具备开启新线程的能力,任务立刻马上执行,会阻塞当前线程并等待
Block
中的业务代码执行完毕dispatch
函数才会返回,然后当前线程才会继续往下运行。 - 异步(Async):可以在新的线程中执行任务,具备开启线程的能力,但不一定会开启新的线程,
dispatch
函数会立即返回, 然后Block
在后台异步执行,即当前线程会直接往下执行,不会阻塞当前线程。
- 同步(Sync):只能在当前线程中执行任务,不具备开启新线程的能力,任务立刻马上执行,会阻塞当前线程并等待
- 队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的
线性表
,采用FIFO(先进先出)
的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。分为串行队列和并行队列。- 串行队列(Serial Dispatch Queue):同一时间内,队列中只能执行一个任务,只有当前的任务执行完成之后(
Block
中的业务代码执行完毕),才能执行下一个任务。 - 并发(并行)队列(Concurrent Dispatch Queue): 同时允许多个任务并发执行(不会等待
Block
中的业务代码执行完毕)。(可以开启多个线程,并且同时执行任务)。并发队列的并发功能只有在异步dispatch_async
函数下才有效。
- 串行队列(Serial Dispatch Queue):同一时间内,队列中只能执行一个任务,只有当前的任务执行完成之后(
串行队列
串行队列里面只会有一个线程,按序执行。
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
并发队列
并发队列可以存在多个线程。
//创建并行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
全局并发队列
本质是一个并发队列,由系统提供,方便编程,可以不用创建就直接使用。
//获取全局并发队列
/**
第一个参数:优先级 也可直接填后面的数字
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
第二个参数: 预留参数 0
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
主队列
本质是一个串行队列,专门负责调度主线程,添加到主队列的任务不会开启新的线程。
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
正确使用线程
以上都是对线程的理解,执行任务需要开启线程,线程在队列里面按序执行,接下来我们看看实际的应用
任务 | 并发队列 | 串行队列 | 主队列 |
---|---|---|---|
同步(sync) | 没有开启新线程 串行执行任务 | 没有开启新线程 串行执行任务 | 串行执行任务 死锁卡住不执行 |
异步(async) | 有开启新线程 并发执行任务 | 有开启新线程(1条) 串行执行任务 | 没有开启新线程 串行执行任务 |
这是六种最基本的排列组合,我们一一讲解。
并发队列
并发 + 同步(sync)
- (void)syncConcurrent
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:syncConcurrent---Begin");
//创建并行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^
// 任务1
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_sync(queue, ^
// 任务2
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_sync(queue, ^
// 任务3
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
);
NSLog(@"wuwuFQ:syncConcurrent---End");
首先明确一点,同步(sync)会阻塞线程等待block回调,并且是不具备开启线程能力的,即使放在并发队列里面,还是需要当前线程也就是主线程去执行任务的
所以结论是:并发+同步 = 没有开启新线程,串行执行任务
并发 + 异步(async)
- (void)asyncConcurrent
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:asyncConcurrent---Begin");
//创建并行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^
// 任务1
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_async(queue, ^
// 任务2
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_async(queue, ^
// 任务3
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
);
NSLog(@"wuwuFQ:asyncConcurrent---End");
这个组合是我们开发过程中经常使用的,异步(async)开启了新线程,并行队列不会阻塞线程,不会等待block的回调,CPU在几个线程之间快速切换,我运行了多次,每次的执行顺序都不通。
所以结论是:并发+异步 = 开启了新线程,并发执行任务
串行队列
串行 + 同步(sync)
- (void)syncSerial
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:syncSerial---Begin");
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^
// 任务1
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_sync(queue, ^
// 任务2
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_sync(queue, ^
// 任务3
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
);
// 任务4
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:4---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:syncSerial---End");
同步不具备开启新线程能力,任务都在主线程按序执行
所以结论是:串行+同步 = 没有开启新线程,串行执行任务
串行 + 异步(async)
- (void)asyncSerial
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:asyncSerial---Begin");
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^
// 任务1
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_async(queue, ^
// 任务2
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_async(queue, ^
// 任务3
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
);
// 任务4
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:4---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:asyncSerial---End");
异步具有开启新线程的能力,但是在串行队列里面任务1、2、3只开启了一个线程,任务1、2、3在子线程按顺序执行,任务4在主线程里面,CPU在两个线程之间快速切换,所以任务4的打印会穿插在任务1、2、3中间,但任务1、2、3的执行顺序不会变。
所以结论是:串行+异步 = 开启了一个新线程,串行执行任务
主队列
主队列 + 同步(sync)
- (void)syncMainQueue
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:syncMainQueue---Begin");
//创建并行队列
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_sync(main_queue, ^
// 任务1
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_sync(main_queue, ^
// 任务2
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_sync(main_queue, ^
// 任务3
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
);
NSLog(@"wuwuFQ:syncMainQueue---End");
主队列负责管理主线程,不会创建新的线程,创建的1、2、3任务遵循
FIFO(先进先出)
插入到队尾最后执行,但是任务是同步执行,所以后面的任务需要等待任务1、2、3的执行,而任务1、2、3却在队列的队尾要等待前面的任务执行,这样线程就会卡主,造成死锁。而程序也会报错:__DISPATCH_WAIT_FOR_QUEUE__
所以结论是:主队列+同步 = 阻塞主线程,死锁
主队列 + 异步(async)
- (void)asyncMainQueue
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:asyncMainQueue---Begin");
//创建并行队列
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(main_queue, ^
// 任务1
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_async(main_queue, ^
// 任务2
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
);
dispatch_async(main_queue, ^
// 任务3
for (int i = 0; i < 2; ++i)
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
);
NSLog(@"wuwuFQ:asyncMainQueue---End");
主队列不会创建新的线程,首先任务1、2、3会被插入到主队列尾部,然后任务异步执行,无需等待block回调,主线程按序执行,最后执行任务1、2、3。
所以结论是:主队列+异步 = 没有开启新线程,串行执行任务
以上是关于iOS开发:多线程之理解任务和队列的主要内容,如果未能解决你的问题,请参考以下文章