【iOS Sharing】【3】多线程相关

本文最后更新于:2021年12月22日 上午

【iOS笔记】系列目录


目录

1、运行以下GCD多线程代码,控制台将打印什么?

2、串行同步、串行异步、并发同步、并发异步各自会开几条线程?

3、为什么需要在主线程更新UI?

4、iOS中如何用多线程实现多读单写?

5、iOS多线程中有多少种方式可以做到等待前面线程执行完毕再执行后面的线程?


1、运行以下GCD多线程代码,控制台将打印什么?

1
2
3
4
5
6
7
8
9
10
11
dispatch_queue_t gQueue= dispatch_get_global_queue(0, 0);

NSLog(@"1");
dispatch_sync(gQueue, ^{
NSLog(@"2");
dispatch_sync(gQueue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");

答案:12345

首先打印1,全局队列本质是并发队列也就是并发同步,同步任务不开起线程,在主线程执行打印2。
然后全局队列执行同步任务,依旧不开启线程,在主线程执行打印3。
同步任务完成,依旧存在于全局队列同步执行,打印4.
同步任务完成,打印5。


2、串行同步、串行异步、并发同步、并发异步各自会开几条线程?

答:

首先了解一些基本概念:

  • 同步:只能在当前线程中执行任务,不具备开启新线程的能力
  • 异步:可以在新的线程中执行任务,具备开启新线程的能力
  • 串行:一个任务执行完毕后,再执行下一个任务
  • 并发:多个任务并发(同时)执行


总结

  • 串行同步不开新线程
  • 串行异步开启一条新线程
  • 并发同步不开新线程
  • 并发异步开启多条

3、为什么需要在主线程更新UI?

答:
  • 安全

在非主线程中更新UI就会有多个线程同时操作一个控件的可能,造成最后更新的结果不符合预期

  • 效率

多线程本身就是为了并发处理以达到高效的目的,但是刷新UI使用并发会造成安全问题,要解决上面的安全问题,那就需要给控件加锁,但是加锁必然会造成额外的开销,同时开新的线程本身就有一定的开销,所以不如直接在主线程中执行更新操作。


4、iOS中如何用多线程实现多读单写?

答:
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
@interface CustomDictionary ()

//多线程需要访问的数据量
@property (nonatomic, strong) NSMutableDictionary *dataDic;

@end

//模拟场景,允许多个线程同时访问字典,但是只有一个线程可以写字典
@implementation CustomDictionary {
//定义一个并发队列
dispatch_queue_t _concurrent_queue;

}

- (instancetype)init {
if (self = [super init]) {
_concurrent_queue = dispatch_queue_create("com.mf.read_write_queue", DISPATCH_QUEUE_CONCURRENT);
_dataDic = @{}.mutableCopy;
}

return self;
}

// 读取数据,并发操作
- (id)objectForKey:(NSString *)key {
__block id obj;
//同步读取数据
dispatch_sync(_concurrent_queue, ^{
obj = [self.dataDic objectForKey:key];
});

return obj;

}

// 写入数据,异步栅栏
- (void)setObject:(id)obj forKey:(NSString *)key {
//异步栅栏调用设置数据
dispatch_barrier_async(_concurrent_queue, ^{
[self.dataDic setObject:obj forKey:key];
});
}

@end


5、iOS多线程中有多少种方式可以做到等待前面线程执行完毕再执行后面的线程?

答:
  • barrier

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    dispatch_queue_t queue = dispatch_queue_create("com.mf.barrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
    NSLog(@"1");
    });

    dispatch_async(queue, ^{
    NSLog(@"2");
    });

    dispatch_barrier_async(queue, ^{
    NSLog(@"等待任务1,2上面执行完毕");
    });

    dispatch_async(queue, ^{
    NSLog(@"3");
    });

    dispatch_async(queue, ^{
    NSLog(@"4");
    });
  • group notify

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 全局变量group
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 进入组(进入组和离开组必须成对出现, 否则会造成死锁)
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
    NSLog(@"1");
    //离开组
    dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
    NSLog(@"2");
    dispatch_group_leave(group);
    });

    dispatch_group_notify(group, queue, ^{ // 监听组里所有线程完成的情况
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"任务1,2已完成");
    });
    });
  • NSOperationQueue Dependency

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//创建队列
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
//创建操作
NSBlockOperation *operation1=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第1次操作,线程:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第2次操作,线程:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第3次操作,线程:%@",[NSThread currentThread]);
}];
//添加依赖
[operation1 addDependency:operation2];
[operation2 addDependency:operation3];
//将操作添加到队列中去
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
  • semaphore
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
/*
比如说我们需要请求三张元素图,拼合成一张海报。我们需要先对元素图进行请求而后才能合成海报,这就形成了依赖关系。我们通过semaphore限制资源数为3,供请求元素图使用,待请求完成后,释放信号量,便能走到合成的耗时操作。
*/

//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);

//元素图1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//等待降低信号量
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"请求第1张元素图");
sleep(1);
NSLog(@"第1张元素图Get");
//提高信号量
dispatch_semaphore_signal(semaphore);
});

//元素图2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"请求第2张元素图");
sleep(1);
NSLog(@"第2张元素图Get");
dispatch_semaphore_signal(semaphore);
});

//元素图3
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"请求第3张元素图");
sleep(1);
NSLog(@"第3张元素图Get");
dispatch_semaphore_signal(semaphore);
});

//合成海报
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"合成海报");
sleep(1);
NSLog(@"海报Get");
dispatch_semaphore_signal(semaphore);
});

注意:

正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。


联系方式

邮箱: adrenine@163.com