底层原理(八)------多线程

iOS中常见的多线程方案

控制器命令:
c:过掉
step:输出汇编
stepi:进入这个汇编函数 简称si

  • NSThread、GCD、NSOperation的底层实现其实都是pthread

GCD的常用函数

  • GCD源码:https://github.com/apple/swift-corelibs-libdispatch

GCD的队列

容易混淆的术语:同步、异步,并发、串行

各种队列的执行效果

// dispatch_sync和dispatch_async用来控制是否要开启新的线程

/**
 队列的类型,决定了任务的执行方式(并发、串行)
 1.并发队列
 2.串行队列
 3.主队列(也是一个串行队列)
 */

面试题

1、下列问题是否会产生死锁

- (void)interview01
{
    // 问题:以下代码是在主线程执行的,会不会产生死锁?会!
    NSLog(@"执行任务1");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"执行任务2");
    });
    
    NSLog(@"执行任务3");
    
    // dispatch_sync立马在当前线程同步执行任务
}

- (void)interview02
{
    // 问题:以下代码是在主线程执行的,会不会产生死锁?不会!
    NSLog(@"执行任务1");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
    });
    
    NSLog(@"执行任务3");
    
    // dispatch_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");

    // block0等block1执行完毕,block1等block0执行完毕
    // 打印顺序:任务1-5-2-死锁
}

- (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_CONCURRENT);
    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_async(queue, ^{ // 0
        NSLog(@"执行任务2");
        
        dispatch_sync(queue, ^{ // 1
            NSLog(@"执行任务3");
        });
        
        NSLog(@"执行任务4");
    });
    
    NSLog(@"执行任务5");
}
  • 队列的特点:排队,FIFO,First In First Out,先进先出
  • dispatch_sync:立马在当前线程执行任务,执行完成才能继续往下执行
  • dispatch_async不要求立马在当前线程同步执行任务

2、下面代码的打印结果是什么?


// 这句代码的本质是往Runloop中添加定时器
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
// 这句代码本质就是Runtime调用objc_msgSend
[self performSelector:@selector(test) withObject:nil];

3、下面打印结果是什么?

- (void)test
{
    NSLog(@"2");
}

// 打印结果为1,因为[thread start]启动会运行当前block,输出结果为1,但是输出以后,线程也就自动退出了,也就是没有子线程了,所以thread=nil,如果想继续执行的话 在block里面启动RunLoop。
//        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1");
//        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }];
    [thread start];
    
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}

GNUstep

  • GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍
  • 源码地址:http://www.gnustep.org/resources/downloads.php
  • 虽然GNUstep不是苹果官方源码,但还是具有一定的参考价值

队列组的使用

思考:如何用gcd实现以下功能
异步并发执行任务1、任务2
等任务1、任务2都执行完毕后,再回到主线程执行任务3

// 创建队列组
    dispatch_group_t group = dispatch_group_create();
    // 创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("my_queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 添加异步任务
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务1-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务2-%@", [NSThread currentThread]);
        }
    });
    
    // 等前面的任务执行完毕后,会自动执行这个任务
//    dispatch_group_notify(group, queue, ^{
//        dispatch_async(dispatch_get_main_queue(), ^{
//            for (int i = 0; i < 5; i++) {
//                NSLog(@"任务3-%@", [NSThread currentThread]);
//            }
//        });
//    });
    
    //也可以直接把主队列放进去
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        for (int i = 0; i < 5; i++) {
//            NSLog(@"任务3-%@", [NSThread currentThread]);
//        }
//    });
    
    // 也可以执行完任务1、2 再去执行任务3、4
    dispatch_group_notify(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_notify(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务4-%@", [NSThread currentThread]);
        }
    });

多线程安全隐患

多线程安全隐患示例01-存钱取钱

存钱取钱代码演示

@property (assign, nonatomic) int money;


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [self moneyTest];
}

/**
 存钱、取钱演示
 */
- (void)moneyTest
{
    self.money = 100;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney
{
    int oldMoney = self.money;
    sleep(.2);
    oldMoney += 50;
    self.money = oldMoney;
    
    NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
}

/**
 取钱
 */
- (void)drawMoney
{
    int oldMoney = self.money;
    sleep(.2);
    oldMoney -= 20;
    self.money = oldMoney;
    
    NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
}
多线程安全隐患示例02-卖票

卖票代码演示

/**
 卖1张票
 */
@property (assign, nonatomic) int ticketsCount;

- (void)saleTicket
{
    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    
    NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
}

/**
 卖票演示
 */
- (void)ticketTest
{
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
             [self saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self ticketTest];
}
多线程安全隐患分析
多线程安全隐患解决方案

iOS 中的线程同步方案

  • OSSpinLock
  • os_unfair_lock
  • pthread_mutex
  • dispatch_semaphore
  • dispatch_queue(DISPATCH_QUEUE_SERIAL)
  • NSLock
  • NSRecursiveLock
  • NSCondition
  • NSConditionLock
  • @synchronized
OSSpinLock

os_unfair_lock

pthread_mutex 普通锁

pthread_mutex--递归锁

递归锁:允许同一个线程对一把锁进行重复加锁


pthread_mutex--条件锁

NSLock、NSRecursiveLock

NSCodition

NSConditionLock

dispatch_queue

dispatch_semaphore

@synchronized

所有示例代码Demo

iOS线程同步方案性能比较

GCD、OperationQueue 对比

GCD的核心概念:将 任务(block) 添加到队列,并且指定执行任务的函数。
NSOperation 的核心概念:把 操作(异步) 添加到 队列。

区别
GCD:
将任务(block)添加到队列(串行/并发/主队列),并且指定任务执行的函数(同步/异步)
GCD是底层的C语言构成的API
iOS 4.0 推出的,针对多核处理器的并发技术
在队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构
要停止已经加入 queue 的 block 需要写复杂的代码
需要通过 Barrier(dispatch_barrier_async)或者同步任务设置任务之间的依赖关系
只能设置队列的优先级
高级功能: dispatch_once_t(一次性执行, 多线程安全); dispatch_after(延迟); dispatch_group(调度组); dispatch_semaphore(信号量); dispatch_apply(优化顺序不敏感大体量for循环);

OperationQueue:
OC 框架,更加面向对象,是对 GCD 的封装。
iOS 2.0 推出的,苹果推出 GCD 之后,对 NSOperation 的底层进行了全部重写。
可以设置队列中每一个操作的 QOS() 队列的整体 QOS
操作相关 Operation作为一个对象,为我们提供了更多的选择: 任务依赖(addDependency),可以跨队列设置操作的依赖关系; 在队列中的优先级(queuePriority) 服务质量(qualityOfService, iOS8+); 完成回调(void (^completionBlock)(void)
队列相关 服务质量(qualityOfService, iOS8+); 最大并发操作数(maxConcurrentOperationCount),GCD 不易实现; 暂停/继续(suspended); 取消所有操作(cancelAllOperations); KVO 监听队列任务执行进度(progress, iOS13+);

自旋锁跟互斥锁比较

atomic

/*
 nonatomic和atomic
 atom:原子,不可再分割的单位
 atomic:原子性
 
 给属性加上atomic修饰,可以保证属性的setter和getter都是原子性操作,也就是保证setter和gette内部是线程同步的
 
 atomic跟nonatomic 在取值跟设值的时候不同,atomic取值设值会加锁(自旋锁)
atomic经常在Mac使用,太耗内存
 */

iOS中的读写安全方案

pthread_rwlock:读写锁

dispatch_barrier_async:异步栅栏调用

你可能感兴趣的:(底层原理(八)------多线程)