前言
GCD源码地址
GNUStep
(是GNU计划项目之一,他将cocoa库重新开源实现了一遍,不是apple官方文档,但是具有一定的参考价值)
常见的多线程方案一般分为这几种
GCD函数
//异步执行任务
dispatch_async(dispatch_get_global_queue(0, 0), ^{
});
//同步执行任务
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
});
同步 异步 串行 并发
- 同步、异步主要影响:能不能开启新线程。
- 串行、并发主要影响:任务执行方式,串行一个任务执行完毕之后,在执行下一个。并发多个任务同时执行。
死锁问题
先看一段代码
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任务2");
});
NSLog(@"任务3");
}
上面一段代码发生了死锁,为什么呢?原因就是:viewDidLoad是在主队列里面执行的,当执行到任务2的时候,因为是同步任务,所以需要立刻执行任务2,但是队列是先进先出的概念,viewDidLoad本身也是一个任务,他需要执行完任务3之后才能执行任务2,但是任务2需要立即执行,也就是说任务3等待任务2执行完毕执行,任务2有等待任务3执行完毕执行,造成了死锁,程序卡死在任务2处。
死锁原理:使用sync函数往当前串行队列(serial quque)添加任务就会产生死锁
[self performSelector:@selector(test) withObject:nil afterDelay:.0]; 延迟方法
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3");
});
}
- (void)test {
NSLog(@"2");
}
---------------------------------------------------
2018-11-29 14:09:46.184533+0800 demodemo[88317:5593889] 1
2018-11-29 14:09:46.185962+0800 demodemo[88317:5593889] 3
延迟方法的底层实现是在runloop内部添加定时器来处理任务的,但是子线程默认是没有启动RunLoop的,所以导致方法失效。
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3");
[[NSRunLoop currentRunLoop]addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
---------------------------------------------------
2018-11-29 14:20:43.557833+0800 demodemo[88643:5608216] 1
2018-11-29 14:20:43.559215+0800 demodemo[88643:5608216] 3
2018-11-29 14:20:43.559472+0800 demodemo[88643:5608216] 2
启动RunLoop,可以执行延迟函数,因为是延迟函数,需要等待下一次被唤醒的时候处理定时器任务,所以3先被执行。
[self performSelector:@selector(test) withObject:nil] 方法的底层原理是[class msgSend],跟延迟方法是不同的。
从GNUStep可以大致推出延迟方法apple的源码实现是这样的
- (void) performSelector: (SEL)aSelector
withObject: (id)argument
afterDelay: (NSTimeInterval)seconds
{
NSRunLoop *loop = [NSRunLoop currentRunLoop];
GSTimedPerformer *item;
item = [[GSTimedPerformer alloc] initWithSelector: aSelector
target: self
argument: argument
delay: seconds];
[[loop _timedPerformers] addObject: item];
RELEASE(item);
[loop addTimer: item->timer forMode: NSDefaultRunLoopMode];
}
队列组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
for (int i = 0; i<5; i++) {
NSLog(@"任务1");
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i<5; i++) {
NSLog(@"任务2");
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成");
});
---------------------------------------------
2018-11-29 15:30:19.442732+0800 demodemo[90541:5689534] 任务2
2018-11-29 15:30:19.442732+0800 demodemo[90541:5689182] 任务1
2018-11-29 15:30:19.443865+0800 demodemo[90541:5689182] 任务1
2018-11-29 15:30:19.443865+0800 demodemo[90541:5689534] 任务2
2018-11-29 15:30:19.444347+0800 demodemo[90541:5689182] 任务1
2018-11-29 15:30:19.444362+0800 demodemo[90541:5689534] 任务2
2018-11-29 15:30:19.444449+0800 demodemo[90541:5689182] 任务1
2018-11-29 15:30:19.444501+0800 demodemo[90541:5689534] 任务2
2018-11-29 15:30:19.444640+0800 demodemo[90541:5689182] 任务1
2018-11-29 15:30:19.445137+0800 demodemo[90541:5689534] 任务2
2018-11-29 15:30:19.446057+0800 demodemo[90541:5689084] 完成
网友评论