多线程

iOS 多线程

1.iOS几种多线程方案?
  • NSOperation (基于GCD)

    • 可以添加任务依赖

    • 任务执行状态的控制

      • isReady 当前任务是否处于就绪状态

      • isExecuting 当前任务是否处于正在执行状态

      • isFinished 当前任务是否已经完成

      • isCancelled 当前任务是否取消

      可以通过重写start、main两个方法进行状态的控制。

      • 如果只重写main方法,底层控制变更任务执行完成状态,以及任务退出

      • 如果重写了start方法,自行控制任务状态

    • 可以控制最大并发量

  • GCD

  • Pthread

  • NSThread

1.1 系统是怎样移除一个isFinished = YES 的NSOperation的?

通过KVO来实现operation的状态控制

2. 理解并发队列,串行队列,同步,异步

队列:是先进先出的线性表。无论是并发队列,还是串行队列,都是按先进先出的顺序执行任务。

  • 串行队列:说明队列中的任务串行执行,也就是一个一个的执行,必须等待上一个任务执行完成后,才能执行下一个任务。

  • 并发队列:同样按照顺序执行任务,区别是,加入并发队列中有a、b、c、d四个任务,会先执行a,在a执行的过程中可以执行b,因此a、b具体哪个先执行完成是不确定的,具体同时能执行几个,由系统控制(GCD中不能直接设置并发数,可以通过信号量的方式实现,NSOperationQueue可以直接设置),但肯定是按照先进先出的原则调用的。

  • 同步:在当前线程中顺序执行任务,执行当前任务完成之后才能继续执行下一个任务。并不会开启新线程。

  • 异步:在新的线程中执行任务,不管调用的函数(任务)有没有执行完,都会进行下一个任务。且具备开启新线程的能力。

同步和异步的区别主要是向队列里面添加任务时是立即返回,还是等添加的任务完成之后再返回。

并发队列 手动创建的串行队列 主队列(串行执行任务)
同步(sync) 没有开启新线程/串行执行任务 没有开启新线程/串行执行任务 没有开启新线程/串行执行任务
异步(async) 有开启新线程/并发执行任务 有开启新线程/串行执行任务 没有开启先线程/串行执行任务

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

2.下面代码会造成什么结果?
- (void)viewDidLoad {
    NSLog(@"1");
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"2");
    });
    NSLog(@"3");
}
// 不会造成死锁  答案:1、2、3
// 因为1、3 跟2 不在同一个队列中
复制代码
- (void)interview01 {
    NSLog(@"执行任务1");
    dispatch_sync(dispatch_get_main_queue(),^{
        NSLog(@"执行任务2");
    });
    NSLog(@"执行任务3");
}
//会造成死锁。同步线程相互等待。
//原因:同步队列中在任务1还没完成插入了任务2任务,
//正常同步队列应该在interview01任务完成后再执行任务3
//上述流程是在完成过程中立马插入任务2任务并执行,因此会造成死锁。
复制代码
- (void)interview02 {
    NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
    });
    NSLog(@"执行任务3");
}
// 不会死锁:主队列是串行执行任务。但是async异步,虽然在主队列不会开辟新的线程
// 但是在串行队列中不要求立马执行async异步任务
// 因此答案:1、3、2
复制代码
- (void)interview03 {
    NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL); // 串行队列
    dispatch_async(queue, ^{ // 0
        NSLog(@"执行任务2");
        dispatch_sync(queue, ^{ // 1
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}

// 会造成死锁
// 串行队列中,先执行1然后因为async异步,所以执行5然后执行2
// 但是在async中 如果想要同步执行3,那么要先执行4,但是队列中又必须先执行3才能执行4
// 所以会死锁
复制代码
- (void)interview04 {
    NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL); // 串行队列
    dispatch_async(queue, ^{ // 0
        NSLog(@"执行任务2");
        dispatch_sync(queue2, ^{ // 1
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}
// 不会死锁 答案:1、5、2、3、4
复制代码
- (void)interview05 {
    NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{ // 0
        NSLog(@"执行任务2");
        dispatch_sync(queue, ^{ // 1
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}
// 不会死锁:并行队列可以同步执行多个任务。答案:1、2、3、4、5
复制代码
- (void)GCD {
    //全局队列:是系统开发的,直接拿过来(get)用就可以;与并行队列类似,但调试时,无法确认操作所在队列
    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");
}
// 打印结果:1 3
// performSelector:withObject:afterDelay:本质是往当前RunLoop中添加定时任务。
// 当前子线程默认没有开启RunLoop,执行3之后线程就销毁
复制代码
- (void)GCD2 {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1");
    }];
    [thread start];
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
// 造成崩溃
// thread,因为线程没有开启RunLoop执行完毕后会释放线程
// performSelector:onThread:withObject:waitUntilDone: 找不到thread会造成崩溃
复制代码

3. 如何实现多读单写?

使用GCD dispatch_barrier (异步栅栏)

4. dispatch_group_async()

  1. 使用GCD实现:A、B、C三个任务并发,完成后执行任务D?
- (void)dispathcGroupAsync {
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, _concurrent, ^{
        NSLog(@"A");
    });
    dispatch_group_async(group, _concurrent, ^{
        NSLog(@"B");
    });
    dispatch_group_async(group, _concurrent, ^{
        NSLog(@"C");
    });
    dispatch_group_notify(group, _concurrent, ^{
        NSLog(@"D");
    });
}
复制代码

5.多线程锁

自旋锁:等待锁的时候处于忙等状态,一直占着CPU资源

互斥锁:等待锁的时候处于休眠状态

条件锁:满足条件锁打开继续运行,不满足条件时就进入休眠

递归锁:特点是可以重复加锁。重入

  • @synchronized

    一般在创建单利对象的时候使用,来保证在多线程环境下创建对象是唯一的

  • atomic

    属性关键字,对被修饰的对象进行原子操作(不负责使用)。如:array = [NSMutableArray array] 赋值可以保证原子性,[array addObject:obj] 对象操作不可以保证原子性。

  • OSSpinLock

    属于自旋锁,目前已不安全,可能会出现优先级反转的问题。如:如果等待锁的线程优先级比较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁。

  • NSLock

    如果同一把锁两次调用lock,那么会造成死锁,可以通过递归锁解决需要多次加锁的操作,或者使用不同的锁。

  • NSRecursiveLock

    递归锁

  • dispatch_semaphore_t 信号量

    dispatch_semaphore_wait:阻塞线程,是一个主动行为

    dispatch_semaphore_singal:唤醒是一个被动行为

  • pthread_mutex

    底层级的锁,NSLock等基本都是基于mutex实现

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享