多线程

多线程

GCD的常用函数

  • 用同步的方式执行任务(在当前线程中执行任务,不具备开启新线程的能力)
    1
    2
    3
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    // queue: 队列
    // block: 任务
  • 用异步的方式执行任务(在新的线程中执行任务,具备开启新线程的能力)
    1
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

GCD的队列

并发队列 Concurrent Dispatch Queue

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

串行队列 Serial Dispatch Queue

  • 让任务一个接着一个执行(一个任务执行完毕,再执行下一个任务)

容易混淆的术语

有4个术语比较容易混淆:同步、异步、并发、串行

  • 同步(dispatch_sync)和异步(dispatch_async)主要影响:能不能开启新的线程
    • dispatch_sync同步:在当前线程中执行任务,不具备开启新线程的能力,同步需要立马拿出来执行,要注意是从队列中
    • dispatch_async异步:在新的线程中执行任务,具备开启新线程的能力,异步不需要立马拿出来执行
      • 在主队列(dispatch_get_main_queue())不会开启新的线程
  • 并发(DISPATCH_QUEUE_CONCURRENT)和串行(DISPATCH_QUEUE_SERIAL)主要影响:任务的执行方式
    • 并发:多个任务并发(同时)执行
    • 串行:一个任务执行完毕后,再执行下一个任务
1
2
3
     | 并发队列 | 手动创建串行队列  | 主队列
同步(sync) | **没有**开启新的线程<br>**串行**执行任务 | **没有**开启新的线程<br>**串行**执行任务 | **没有**开启新的线程<br>**串行**执行任务
异步(async) | **有**开启新的线程<br>**并行**发行执行任务 | **有**开启新的线程<br>**串行**执行任务 | **没有**开启新的线程<br>**串行**执行任务

使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 死锁
- (void)interview01 {
// 在主线程执行 interview01
// 队列先进先出
dispatch_queue_t queue = dispatch_get_main_queue();
// queue 队列
// interview01 在**主队列中**,执行 log123 需要上一个任务执行完毕,
// 而上一个任务 interview01 执行完毕,又需要 log123 执行,因为是 sync 函数,必须里面在当前线程执行任务,执行完毕才能继续往下执行
dispatch_sync(queue, ^{
NSLog(@"123");
});
// dispatch_sync:立马在当前线程执行任务,执行完毕才能继续往下执行
}
// 不会死锁
- (void)interview02 {
// 在主线程执行
// 在主线程执行
NSLog(@"123");
dispatch_queue_t queue = dispatch_get_main_queue();
// 一定是 123 -> 1234 -> block 里面
// 都在主队列中,串行执行任务,由于 async 不会卡住,所以等 interview02 任务执行完,再执行 block 里面
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"1234");
// dispatch_async:不要求立马在当前线程同步执行任务
}
// 会死锁
- (void)interview03 {
// 在主线程执行
NSLog(@"1");
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("com.gcd.myqueue", DISPATCH_QUEUE_SERIAL);
// 开辟了线程
dispatch_async(queue, ^{ // 0
// 子线程执行
NSLog(@"2");
// 1 需要立马从串行队列 queue 拿出来执,但现在还在执行 0,0执行完的前提是1要执行,因为 1 是 sync 需要立马执行
dispatch_sync(queue, ^{ // 1
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}

// 不会死锁
- (void)interview04 {
// 在主线程执行
NSLog(@"1");
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("com.gcd.myqueue", DISPATCH_QUEUE_SERIAL);
// 并行
dispatch_queue_t queue2 = dispatch_queue_create("com.gcd.myqueue", DISPATCH_QUEUE_CONCURRENT);
// 开辟了线程
dispatch_async(queue, ^{ // 0
// 子线程执行
NSLog(@"2");
// 不会开启线程 因为是 sync
dispatch_sync(queue2, ^{ // 1
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
// 不会死锁
- (void)interview05 {
// 在主线程执行
NSLog(@"1");
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("com.gcd.myqueue", DISPATCH_QUEUE_SERIAL);
// 并行
dispatch_queue_t queue2 = dispatch_queue_create("com.gcd.myqueue", DISPATCH_QUEUE_CONCURRENT);
// 开辟了线程
dispatch_async(queue, ^{ // 0
// 子线程执行
NSLog(@"2");
// 不会开启线程 因为是 sync,不会死锁因为是 并发队列一起执行,但一定是3先执行再执行4,因为sync同步需要立马拿出来执行
dispatch_sync(queue2, ^{ // 1
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}

面试题

下面打印的结果是什么

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
- (void)test {
NSLog(@"2");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
// 子线程默认没有开启 RunLoop
// performSelector:withObject:afterDelay: 底层是定时器,并且已经往 RunLoop 中添加了这个定时器添 只是没有 跑起来 runloop

[self performSelector:@selector(test) withObject:nil afterDelay:0];
});
NSLog(@"3");
}
// log: 1 3

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 开启一个子线程,子线程默认不启动RunLoop
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1");
}];
[thread start];
// 给子线程发消息,需要RunLoop
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:true];

}
// log: 1 并且崩溃