iOS 多线程 基本概念

前言

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] 完成

你可能感兴趣的:(iOS 多线程 基本概念)