必备知识架构-线程与网络-①队列与任务或线程

[toc]

知识架构

iOS知识库

Android知识库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>1、死锁探究:GCD死锁及报错提示(EXC_BAD_INSTRUCTION)
>(1)、死锁举例:通过串行队列里的任务,往这个串行队列里添加同步任务,会造成死锁
>(2)、死锁结论:往串行队列里添加的同步任务不能卡住该串行队列,否则会造成死锁(这句话非常重要)
>(3)、死锁原因分析
>
>2、主队列中的死锁:在主队列开启同步任务,一定为什么会阻塞线程?
>
>3、iOS多线程中,队列和执行的排列组合结果分析
>(1)、队列的类型
>(2)、四个比较容易混淆的术语:同步、异步、并发、串行
>(3)、队列和线程的关系
>(4)、自己延伸的问题:一个队列同时存在同步任务和异步任务?
>
>4、并发是并发队列,并行又是什么鬼???
>(1)、多个处理器和多核处理器的区别
>(2)、并发和并行的区别
>
>5、进程和线程的区别
>(1)、进程和线程的区别
>(2)、一个app运行时只有一个进程吗?有没有多进程?
>(3)、什么时候用进程?什么时候用线程?
>

五、队列与任务执行

< 返回目录

1、队列的类型(并发队列(Concurrent Dispatch Queue)& 串行队列(Serial Dispatch Queue))

GCD的队列可以分为2大类型:并发队列和串行队列。

(1)并发队列(Concurrent Dispatch Queue)

可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效
并发队列

####(2)串行队列(Serial Dispatch Queue)

让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
串行队列

问:并发是并发队列,并行又是什么鬼???

答案:下文会详细介绍。

2、队列是用来干嘛的?说一说【队列与任务 】或【队列与线程】的关系?(重要)

答:

1、队列是用来添加任务/保存以及管理任务的,它只是负责任务的调度(是按串行队列还是按并发队列调度),而不负责任务的执行
2、任务是添加到队列里的,可以添加同步任务或者异步任务。

往队列里面添加任务,
①如果是添加异步任务,则会开启新的线程工作,开启的线程数根据队列类型决定。串行只开一条,并行开多条。
②如果是添加同步任务,会在当前线程内工作,不会创建新的线程。

3、对队列所添加的任务是在线程中执行的/由线程负责进行执行,至于在什么线程根据是同步任务还是异步任务,以及当前是什么队列共同决定。

(所以不管是将同步任务添加到串行队列还是并行队列,因为同步任务都不会创建新线程。所以并行同步队列和串行同步队列其实是一样的,它们都不会创建新的线程而且会是顺序执行)

4、补充:在一个线程内可能有多个队列。

2.1、理解队列是用来添加任务/保存以及管理任务的,它只是负责任务的调度,而不负责任务的执行的代码

看以下代码,我们通过它来方便我们理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//  NSOperationQueueCJHelper.m

/**
* 创建队列
*
* @param operations 队列的操作数组
* @param lastOperation 队列的最后一条(最后一条,会等到前面都结束后才会结束)
*
* return 队列
*/
+ (NSOperationQueue *)createOperationQueueWithOperations:(NSArray<NSOperation *> *)operations lastOperation:(NSOperation *)lastOperation {

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"this is a queue";

for (NSBlockOperation *operation in operations) {
[queue addOperation:operation];

[lastOperation addDependency:operation];
}

[queue addOperation:lastOperation];

return queue;
}

// 或者
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(myTask) object:nil];
[operationQueue addOperation:operation];

附:队列的取消、暂停、恢复
取消队列的所有操作使用NSOperationQueue的 - (void)cancelAllOperations;

也可以调用NSOperation的- (void)cancel方法取消单个操作

暂停和恢复队列

- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列

2.2、iOS主线程、主队列(串行队列)、全局队列

iOS 主队列 全局队列

主队列是系统为我们创建的串行队列,而且每个APP只有一个主队列,负责调度处理APP的唯一主线程的UI事件。

注意:主队列没有办法开辟新的线程。所以,在主队列下的任务不管是异步任务还是同步任务都不会开辟线程,任务只会在主线程顺序执行。

主队列异步任务:先将任务放在主队列中,但是不是马上执行,等到主队列中的其它所有除我们使用代码添加到主队列的任务的任务都执行完毕之后才会执行我们使用代码添加的任务。

主队列同步任务:容易阻塞主线程,所以不要这样写。原因:我们自己代码任务需要马上执行,但是主线程正在执行代码任务的方法体,因此代码任务就必须等待,而主线程又在等待代码任务的完成好去完成下面的任务,因此就形成了相互等待。整个主线程就被阻塞了。

全局队列:本质是一个并发队列,由系统提供,方便编程,可以不用创建就直接使用。

2.3、插入问:NSOperationQueue是什么队列?

答:NSOperationQueue是操作队列。结合实际才知道它是并发队列,还是串行队列。

[NSOperationQueue mainQueue]是主队列,和GCD中的主队列一样,是串行队列

[[NSOperationQueue alloc]init]是非主队列,非常特殊(同时具备并发和串行的功能)。默认情况下,非主队列是并发队列。

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
当operationQueue.maxConcurrentOperationCount > 1,那么operationQueue就是并发队列;
当operationQueue.maxConcurrentOperationCount == 1,那么operationQueue就是串行队列。

3、总结四个比较容易混淆的术语:并发队列、串行队列、同步任务、异步任务

3.1、并发和串行决定了任务的执行方式(队列)。

并发队列:多个任务并发(同时)执行,指一个处理器同时处理多个任务
串行队列:一个任务执行完毕后,再执行下一个任务

3.2、同步和异步决定了要不要开启新的线程(线程)

1
2
3
>同步:在当前线程中执行任务,不具备开启新线程的能力
>异步:在新的线程中执行任务,具备开启新线程的能力
>

4、并发是并发队列,并行又是什么鬼(多核处理器同时执行任务)???

(1)、多个处理器和多核处理器的区别

多个处理器:多个单核处理器,就是说电脑和处理器有多个,但是这个电脑的处理器是单核的;

多核处理器:单个多核处理器,也就是说电脑有一个处理器,但是这个处理器是多核的;

附:在生活中,我们的电脑上常见的处理器都是单处理器,但是这个处理器是多核的,一般是四核,有些需要玩高级游戏的,可能还是八核的处理器。

详情查看:多个处理器和多核处理器的区别

(2)、并发和并行的区别

并发(concurrency)是指一个处理器同时处理多个任务。即并发指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。

并发(concurrency).jpg)

并行(parallel)是指多个处理器或者是多核的处理器同时处理多个不同的任务。即并行是指在同一时刻,有多条指令在多个处理器上同时执行。

并行(parallel).jpg)

个人理解:是否可以理解为在出现多核处理器或者只有一个处理器的时候,我们那时候就只还有并发的概念。

并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在,并发能够在单处理器系统中存在是因为并发是并行的假象,并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作(每个小时间片执行一个操作,多个操作快速切换执行)

5、iOS多线程中,队列和执行的排列组合结果分析

该文章非常重要,且主要总结点有

1
2
3
4
5
6
7
1. 开不开线程,取决于执行任务的函数,同步不开,异步开。
2. 开几条线程,取决于队列,串行开一条,并发可以开多条(即并发队列里有异步任务,则可以开多条,如果都只有同步任务,那由于同步任务并不会开新线程,所以此时也就不会开新线程)
3. 主队列:专门用来在主线程上调度任务的"队列",主队列不能在其他线程中调度任务!
4. 如果主线程上当前正在有执行的任务,主队列暂时不会调度任务的执行!主队列同步任务,会造成死锁。原因是循环等待
5. 同步任务可以队列调度多个异步任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这是依赖关系。
6. 全局队列:并发,能够调度多个线程,执行效率高,但是相对费电。 串行队列效率较低,省电省流量,或者是任务之间需要依赖也可以使用串行队列。
7. 也可以通过判断当前用户的网络环境来决定开的线程数。WIFI下6条,3G/4G下2~3条。

6、代码:使用GCD为队列添加任务

实例1:并发队列添加异步任务/用异步函数往并发队列中添加任务的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)viewDidLoad
{
[super viewDidLoad];

//1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//2.添加任务到队列中,就可以执行任务
//异步函数:具备开启新线程的能力
dispatch_async(queue, ^{
​ NSLog(@"下载图片1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
​ NSLog(@"下载图片2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
​ NSLog(@"下载图片2----%@",[NSThread currentThread]);
});
//打印主线程
NSLog(@"主线程----%@",[NSThread mainThread]);

}
上述方法说明.GCD中有2个用来执行任务的函数

说明:把右边的参数(任务)提交给左边的参数(队列)进行执行

(1)用同步的方式执行任务 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

(2)用异步的方式执行任务 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

常见笔试/面试题

< 返回目录

2、以下两种GCD队列创建有什么不同?
1
2
3
4
5
6
7
dispatch_queue_t queue = dispatch_queue_create("MyQueue",DISPATCH_QUEUE_SERIAL);

dispatch_queue_t queue =dispatch_queue_create(@“MyQueue", DISPATCH_QUEUE_CONCURRENT);

//生成一个串行队列,队列中的block按照先进先出(FIFO)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称,在调试程序时会非常有用,所有尽量不要重名了。

//生成一个并发执行队列,block被分发到多个线程去执行

END

< 返回目录