多线程

一、iOS中常见的多线程方案

二、GCD
  1. GCD中有2个用来执行任务的函数
    • 用同步的方式执行任务
      void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
      • queue:队列
      • block:任务
    • 用异步的方式执行任务 void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    • GCD源码:https://github.com/apple/swift-corelibs-libdispatch
  2. GCD的队列
    • 并发队列(Concurrent Dispatch Queue)
      • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
      • 并发功能只能在异步(dispatch_async)函数下才有效
    • 串行队列(Serial Dispatch Queue)
      • 让任务一个接着一个的执行(一个任务执行完毕后,再执行下一个任务)
  3. 容易混淆的术语

    • 有4个术语比较容易混淆:同步、异步、串行、并发
      • 同步和异步主要影响:能不能开启新的线程
        • 同步:在当前线程中执行任务,不具备开启新线程的能力
        • 异步:在新的线程中执行任务,具备开启新线程的能力
      • 并发和串行主要影响:任务的执行方式
        • 并发:多个任务并发(同时)执行
        • 串行:一个任务执行完毕后,再执行下一个任务
  4. 各种队列的执行效果

    判断是否产生死锁:使用sync函数往当前串行队列中添加任务,会产生死锁

  5. 队列组的使用

    • 异步并发执行任务1、任务2
    • 等任务1、任务2都执行完毕后,再回到主线程执行任务3
    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 < 3; i++) {
    NSLog(@"任务1 - %@",[NSThread currentThread]);
    }
    });
    dispatch_group_async(group, queue, ^{
    for (int i = 0; i < 3; i++) {
    NSLog(@"任务2 - %@",[NSThread currentThread]);
    }
    });
    dispatch_group_notify(group, queue, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
    for (int i = 0; i < 3; i++) {
    NSLog(@"任务3 - %@",[NSThread currentThread]);
    }
    });
    });
三、线程安全
  1. 多线程安全隐患
    • 资源共享
      • 一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
      • 比如多个线程访问同一个对象、同一个变量、同一个文件
    • 多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题(卖票问题、存取钱问题)
  2. 解决方案
    • 使用线程同步技术(同步,就是协同步调,按预定的先后次序进行)
    • 常见的线程同步技术:加锁
  3. 加锁前
  4. 加锁后
四、线程同步方案
  1. OSSpinLock

    • OSSpinLock叫做“自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
    • 目前已经不再安全,可能会出现优先级反转问题

      • 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
      • 需要导入头文件#import <libkern/OSAtomic.h>
      @property (nonatomic, assign) OSSpinLock moneyLock;
      self.moneyLock = OS_SPINLOCK_INIT; // 初始化
      bool result = OSSpinLockTry(&_moneyLock); // 尝试加锁(如果需要等待就不加锁,直接返回false;如果不需要等待就加锁,返回true)
      OSSpinLockLock(&_moneyLock); // 加锁
      OSSpinLockUnlock(&_moneyLock); // 解锁
  2. os_unfair_lock

    • os_unfair_lock用于取代不安全的OSSpinLock,从iOS10才开始支持
    • 从底层调用来看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
    • 需要导入头文件#import <os/lock.h>
    @property (nonatomic, assign) os_unfair_lock moneyLock;
    self.moneyLock = OS_UNFAIR_LOCK_INIT;
    os_unfair_lock_lock(&_moneyLock);
    os_unfair_lock_unlock(&_moneyLock);
  3. pthread_mutex

    • mutex叫做“互斥锁”,等待锁的线程会处于休眠状态
    • 需要导入头文件#import <pthread.h>
    /*
     * Mutex type attributes
    */
    #define PTHREAD_MUTEX_NORMAL 0
    #define PTHREAD_MUTEX_ERRORCHECK 1
    #define PTHREAD_MUTEX_RECURSIVE 2 // 递归锁
    #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
    @property (nonatomic, assign) pthread_mutex_t moneyMutex;
    - (void)__initMutex:(pthread_mutex_t *)mutex {
    // 初始化锁的属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    // 初始化锁
    pthread_mutex_init(mutex, &attr);
    // 销毁锁的属性
    pthread_mutexattr_destroy(&attr);
    }
    // 加锁
    pthread_mutex_lock(&_moneyMutex);
    // 解锁
    pthread_mutex_unlock(&_moneyMutex);
    // 销毁锁
    pthread_mutex_destroy(&_moneyMutex);
    pthread_mutex_destroy(&_ticketMutex);
  4. pthread_mutex - 条件

    #import <pthread.h>
    @interface MutexDemo3 ()
    @property (nonatomic, assign) pthread_mutex_t mutex;
    @property (nonatomic, assign) pthread_cond_t cond;
    @property (nonatomic, strong) NSMutableArray *data;
    @end
    @implementation MutexDemo3
    - (instancetype)init
    {
    self = [super init];
    if (self) {
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    pthread_mutex_init(&_mutex, &attr);
    pthread_mutexattr_destroy(&attr);
    self.data = [NSMutableArray array];
    pthread_cond_init(&_cond, NULL);
    }
    return self;
    }
    - (void)otherTest {
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
    }
    - (void)__remove {
    pthread_mutex_lock(&_mutex);
    if ([self.data count] == 0) {
    pthread_cond_wait(&_cond, &_mutex);
    NSLog(@"移除数据 - %@",[NSThread currentThread]);
    }
    [self.data removeLastObject];
    pthread_mutex_unlock(&_mutex);
    }
    - (void)__add {
    pthread_mutex_lock(&_mutex);
    [self.data addObject:@"test"];
    NSLog(@"添加了数据 - %@",[NSThread currentThread]);
    pthread_cond_signal(&_cond);
    // pthread_cond_broadcast(&_cond); // 通知所有等待的锁
    pthread_mutex_unlock(&_mutex);
    }
    - (void)dealloc
    {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
    }
    @end
  5. NSLock、NSRecursiveLock

    • NSLock是对mutex普通锁的封装
    @property (nonatomic, strong) NSLock *moneyLock;
    self.moneyLock = [[NSLock alloc] init];
    [self.moneyLock lock];
    [self.moneyLock unlock];
    • NSRecursiveLock是对mutex递归锁的封装
  6. NSCondition是对mutex和cond的封装

    #import "NSConditionDemo.h"
    @interface NSConditionDemo ()
    @property (nonatomic, strong) NSCondition *condition;
    @property (nonatomic, strong) NSMutableArray *data;
    @end
    @implementation NSConditionDemo
    - (instancetype)init
    {
    self = [super init];
    if (self) {
    self.condition = [[NSCondition alloc] init];
    self.data = [NSMutableArray array];
    }
    return self;
    }
    - (void)otherTest {
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
    }
    - (void)__remove {
    [self.condition lock];
    if (self.data.count == 0) {
    [self.condition wait];
    }
    [self.data removeLastObject];
    NSLog(@"删除了数据 - %@",[NSThread currentThread]);
    [self.condition unlock];
    }
    - (void)__add {
    [self.condition lock];
    [self.data addObject:@"test"];
    NSLog(@"添加了数据 - %@",[NSThread currentThread]);
    [self.condition signal];
    // [self.condition broadcast];
    [self.condition unlock];
    }
    - (void)dealloc
    {
    NSLog(@"%s",__func__);
    }
    @end
  7. NSConditionLock

    • NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值
    #import "NSConditionLockDemo.h"
    @interface NSConditionLockDemo ()
    @property (nonatomic, strong) NSConditionLock *condLock;
    @end
    @implementation NSConditionLockDemo
    - (instancetype)init
    {
    self = [super init];
    if (self) {
    // self.condLock = [[NSConditionLock alloc] initWithCondition:0];
    self.condLock = [[NSConditionLock alloc] init]; // 默认condition是0
    }
    return self;
    }
    - (void)otherTest {
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
    }
    - (void)__one {
    // [self.condLock lockWhenCondition:0];
    [self.condLock lock]; // 不管condition是多少,都能加锁
    NSLog(@"one - %@",[NSThread currentThread]);
    [self.condLock unlockWithCondition:1];
    }
    - (void)__two {
    [self.condLock lockWhenCondition:1];
    NSLog(@"two - %@",[NSThread currentThread]);
    [self.condLock unlockWithCondition:2];
    }
    - (void)__three {
    [self.condLock lockWhenCondition:2];
    NSLog(@"three - %@",[NSThread currentThread]);
    [self.condLock unlock];
    }
    - (void)dealloc
    {
    NSLog(@"%s",__func__);
    }
    @end
  8. dispatch_semaphore

    • semaphore叫做“信号量”
    • 信号量的初始值,可以用来控制线程并发访问的最大数量
    • 信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
    #import "SemaphoreDemo.h"
    @interface SemaphoreDemo ()
    @property (nonatomic, strong) dispatch_semaphore_t semaphore;
    @property (nonatomic, strong) dispatch_semaphore_t moneySemaphore;
    @property (nonatomic, strong) dispatch_semaphore_t ticketSemaphore;
    @end
    @implementation SemaphoreDemo
    - (instancetype)init
    {
    self = [super init];
    if (self) {
    self.semaphore = dispatch_semaphore_create(5);
    self.moneySemaphore = dispatch_semaphore_create(1);
    self.ticketSemaphore = dispatch_semaphore_create(1);
    }
    return self;
    }
    - (void)otherTest {
    for (int i = 0; i < 15; i++) {
    [[[NSThread alloc] initWithTarget:self selector:@selector(__test) object:nil] start];
    }
    }
    - (void)__test {
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"%@",[NSThread currentThread]);
    sleep(2);
    dispatch_semaphore_signal(self.semaphore);
    }
    - (void)__saveMoney {
    dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
    [super __saveMoney];
    dispatch_semaphore_signal(self.moneySemaphore);
    }
    - (void)__drawMoney {
    dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
    [super __drawMoney];
    dispatch_semaphore_signal(self.moneySemaphore);
    }
    - (void)__saleTicket {
    dispatch_semaphore_wait(self.ticketSemaphore, DISPATCH_TIME_FOREVER);
    [super __saleTicket];
    dispatch_semaphore_signal(self.ticketSemaphore);
    }
    - (void)dealloc
    {
    NSLog(@"%s",__func__);
    }
    @end
  9. synchronized

    • @synchronized是对mutex递归锁的封装
    • 源码查看:objc4的objc-sync.mm文件
    • @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
    - (void)__saleTicket {
    static NSObject *lock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    lock = [[NSObject alloc] init];
    });
    @synchronized (lock) {
    [super __saleTicket];
    }
    }
    - (void)otherTest {
    NSLog(@"%s",__func__);
    @synchronized (self) {
    [self otherTest];
    }
    }
  10. 线程同步方案的性能比较

    • 性能从高到低排序
      • os_unfair_lock
      • OSSpinLock
      • dispatch_semaphore
      • pthread_mutex
      • dispatch_queue(DISPATCH_QUEUE_SERIAL)
      • NSLock
      • NSCondition
      • pthread_mutex(recursive)
      • NSRecursiveLock
      • NSConditionLock
      • @synchronized
  11. 自旋锁、互斥锁比较

    • 什么情况使用自旋锁?
      • 预计线程等待锁的时间很短
      • 加锁的代码(临界区)经常被调用,但竞争情况很少发生
      • CPU资源不紧张
      • 多核处理器
    • 什么情况使用互斥锁?
      • 预计线程等待锁的时间比较长
      • 单核处理器
      • 临界区有IO操作
      • 临界区代码复杂或者循环量大
      • 临界区竞争非常激烈
        #### 五、atomic
  12. atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁

  13. 可以参考源码objc4的objc-accessors.mm

  14. 并不能保证使用属性的过程是线程安全的

    六、iOS中的读写安全方案
  15. 多读单写

    • 场景
      • 同一时间,只能有1个线程进行写的操作
      • 同一时间,允许有多个线程进行读的操作
      • 同一时间,不允许既有写的操作,又有读的操作
    • 多读单写,经常用于文件数据的读写操作,实现方案有:
      • pthread_rwlock:读写锁
      • dispatch_barrier_async:异步栅栏调用
  16. pthread_rwlock

    #import "RWLockDemo.h"
    #import <pthread.h>
    @interface RWLockDemo ()
    @property (nonatomic, assign) pthread_rwlock_t rwLock;
    @end
    @implementation RWLockDemo
    - (instancetype)init
    {
    self = [super init];
    if (self) {
    pthread_rwlock_init(&_rwLock, NULL);
    }
    return self;
    }
    - (void)otherTest {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
    [self read];
    });
    dispatch_async(queue, ^{
    [self write];
    });
    }
    }
    - (void)read {
    pthread_rwlock_rdlock(&_rwLock);
    sleep(1);
    NSLog(@"%s",__func__);
    pthread_rwlock_unlock(&_rwLock);
    }
    - (void)write {
    pthread_rwlock_wrlock(&_rwLock);
    sleep(1);
    NSLog(@"%s",__func__);
    pthread_rwlock_unlock(&_rwLock);
    }
    - (void)dealloc
    {
    pthread_rwlock_destroy(&_rwLock);
    }
    @end
  17. dispatch_barrier_async

    • 这个函数传入的并发队列必须是自己通过dispatch_queue_create创建的
    • 如果传入的是一个串行队列或者全局的并发队列,那这个函数便等同于dispatch_async函数的效果
    #import "BarrierDemo.h"
    @interface BarrierDemo ()
    @property (nonatomic, strong) dispatch_queue_t rwLock;
    @end
    @implementation BarrierDemo
    - (instancetype)init
    {
    self = [super init];
    if (self) {
    self.rwLock = dispatch_queue_create("rw-queue", DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
    }
    - (void)otherTest {
    for (int i = 0; i < 10; i++) {
    [self read];
    [self read];
    [self read];
    [self write];
    }
    }
    - (void)read {
    dispatch_async(self.rwLock, ^{
    sleep(1);
    NSLog(@"read");
    });
    }
    - (void)write {
    dispatch_barrier_async(self.rwLock, ^{
    sleep(1);
    NSLog(@"write");
    });
    }
    @end

--Posted from Rpc

转载于:https://my.oschina.net/u/4112912/blog/3049052

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值