iOS的多线程安全隐患与线程同步方案(1)

*   [2.2、os\_unfair\_lock 互斥锁](about:blank#22os_unfair_lock__37)

*   [2.3、pthread\_mutex](about:blank#23pthread_mutex_43)

*   *   [2.3.1、pthread\_mutex - 普通锁](about:blank#231pthread_mutex___47)

    *   [2.3.2、pthread\_mutex – 条件锁](about:blank#232pthread_mutex___80)

    *   [2.3.3、pthread\_mutex – 递归锁](about:blank#233pthread_mutex___112)

    *   [2.3.4、NSLock、NSRecursiveLock](about:blank#234NSLockNSRecursiveLock_131)

    *   [2.3.5、NSCondition、NSConditionLock](about:blank#235NSConditionNSConditionLock_137)

*   [2.4、dispatch\_semaphore 信号量](about:blank#24dispatch_semaphore__172)

*   [2.4、dispatch\_queue](about:blank#24dispatch_queue_193)

*   [2.5、@synchronized(不推荐)](about:blank#25synchronized_223)

*   [2.6、atomic 原子性(不推荐)](about:blank#26atomic__229)

*   [2.7、读写安全](about:blank#27_234)

*   *   [2.7.1、pthread\_rwlock\_t - 读写锁](about:blank#271pthread_rwlock_t___246)

    *   [2.7.2、dispatch barrier - 异步栅栏](about:blank#272dispatch_barrier___273)
  • 3、线程同步方案性能比较

  • 4、问答拓展

    • 4.1、GNUstep

    • 4.2、线程安全的处理手段有哪些?

    • 4.3、OC你了解的锁有哪些?

      • 4.3.1、自旋和互斥对比?

      • 4.3.2、使用以上锁需要注意哪些?

      • 4.3.3、用C/OC/C++,任选其一,实现自旋或互斥?口述即可!

1、多线程的安全隐患

=============================================================================

隐患分析:

在这里插入图片描述

解决方案:

1、使用线程同步技术(同步,就是协同步调,按预定的先后次序进行)

2、常见的线程同步技术是:加锁

在这里插入图片描述

经典案例:买票、存钱取钱、生产消费关系。

2、多线程同步解决方案

==============================================================================

OSSpinLock

os_unfair_lock

pthread_mutex

dispatch_semaphore

dispatch_queue(DISPATCH_QUEUE_SERIAL)

NSLock

NSRecursiveLock

NSCondition

NSConditionLock

@synchronized

2.1、OSSpinLock 自旋锁


1、OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源。

2、目前已经不再安全,可能会出现优先级反转问题。

3、如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁。

4、需要导入头文件#import <libkern/OSAtomic.h>

在这里插入图片描述

2.2、os_unfair_lock 互斥锁


1、os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持。

2、从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等。

3、需要导入头文件#import <os/lock.h>

在这里插入图片描述

2.3、pthread_mutex


1、mutex叫做”互斥锁”,等待锁的线程会处于休眠状态。

2、需要导入头文件#import <pthread.h>

2.3.1、pthread_mutex - 普通锁


 // 初始化锁的属性

 pthread_mutexattr_t attr;

 pthread_mutexattr_init(&attr);

 /**

 * @param attr:参数

 * @param type:锁的类型,传NULL也是默认

 */

 pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);

 

 //锁的类型

 #define PTHREAD_MUTEX_NORMAL 0 // 普通锁

 #define PTHREAD_MUTEX_ERRORCHECK 1 // 检测错误的锁

 #define PTHREAD_MUTEX_RECURSIVE 2  // 递归锁

 #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL

 

 // 初始化锁

 pthread_mutex_t mutex;

 pthread_mutex_init(&mutex,&attr);

 // 尝试加锁

 pthread_mutex_trylock(&mutex);

 // 加锁

 pthread_mutex_lock(&mutex);

 // 解锁

 pthread_mutex_unlock(&mutex);

 // 销毁相关资源

 pthread_mutexattr_destroy(&attr);

 pthread_mutex_destroy(&mutex);





2.3.2、pthread_mutex – 条件锁


    // 初始化属性

    pthread_mutexattr_t attr;

    pthread_mutexattr_init(&attr);

    // NULL代表默认属性

    pthread_mutexattr_settype(&attr, NULL);

    // 初始化锁

    pthread_mutex_t mutex;

    pthread_mutex_init(&mutex, &attr);

    // 销毁属性

    pthread_mutexattr_destroy(&attr);

    

    // 初始化条件

    pthread_cond_t cond;

    pthread_cond_init(&cond, NULL);

    

    // 等待条件(进入休眠,放开mutex锁;被唤醒后,会再次对mutex加锁)

    pthread_cond_wait(&cond, &mutex);

    // 激活一个等待该条件的线程

    pthread_cond_signal(&cond);

    // 激活所有等待该条件的线程

    pthread_cond_boradcast(&cond);

    

    // 销毁相关资源

    pthread_mutex_destroy(&mutex);

    pthread_cond_destroy(&cond);



适用案例:生产-消费模式

2.3.3、pthread_mutex – 递归锁

如果线程1已经对这把锁进行加锁了,线程2也调用发现这把锁被别的线程加锁了,所以线程2就不能加锁,线程2就会在这里等待。


    // 初始化这把锁带有的属性

    pthread_mutexattr_t attr;

    pthread_mutexattr_init(&attr);

    // 设置这把锁属性的类型:

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

    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    

    // 初始化锁

    pthread_mutex_t mutex;

    pthread_mutex_init(mutex, &attr);

    

    // 销毁属性

    pthread_mutexattr_destroy(&attr);

    // 销毁锁

    pthread_mutex_destroy(&mutex);



2.3.4、NSLock、NSRecursiveLock

1、NSLock是对mutex普通锁的封装。

在这里插入图片描述2、NSRecursiveLock是对mutex递归锁的封装,API跟NSLock基本一致。

2.3.5、NSCondition、NSConditionLock

1、NSCondition是对条件锁(mutex和cond)的封装。

在这里插入图片描述

2、NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值

在这里插入图片描述


@interface Demo()

@property (strong, nonatomic) NSConditionLock *conditionLock;

@end



@implementation Demo

- (instancetype)init {

    if (self = [super init]) {

        self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];

    }

    return self;

}



- (void)test {

		// 1、当首个加锁触发没有添加条件时,那么在初始化的时候,不管设置什么条件,都能正常执行

    [self.conditionLock lock];

    // 2、如果有加条件,那么必须跟设置的条件匹配

    // [self.conditionLock lockWhenCondition:1];

    

    NSLog(@"test");

    sleep(1);

    

    [self.conditionLock unlockWithCondition:2];

}

@end



2.4、dispatch_semaphore 信号量


1、semaphore叫做”信号量”。

2、信号量的初始值,可以用来控制线程并发访问的最大数量。

3、信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步。


		// 信号量初始值

    int value = 5;

    // 初始化信号量

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);

    /**

     * 1、如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码

     * 2、如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成 > 0,就让信号量的值减1,然后继续往下执行代码

     */

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);



    // 让信号量的值加1

    dispatch_semaphore_signal(semaphore);



2.4、dispatch_queue


直接使用GCD的串行队列,也是可以实现线程同步。


@interface Demo()

@property (strong, nonatomic) dispatch_queue_t myQueue;

@end



@implementation Demo

- (instancetype)init {

    if (self = [super init]) {

        self.myQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

    }

    return self;

}

- (void)test1 {

    dispatch_sync(self.myQueue, ^{

        NSLog(@"%s",__func__);

    });

}



- (void)test2 {

    dispatch_sync(self.myQueue, ^{

        NSLog(@"%s",__func__);

    });

}

@end



2.5、@synchronized(不推荐)


1、@synchronized是对mutex递归锁的封装。

2、源码查看:objc4中的objc-sync.mm文件。

3、@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作。

在这里插入图片描述

2.6、atomic 原子性(不推荐)


1、atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁。

2、可以参考源码objc4的objc-accessors.mm。

3、不能保证使用属性的过程是线程安全的。

2.7、读写安全


场景:

1、同一时间,只能有1个线程进行写的操作

2、同一时间,允许有多个线程进行读的操作

3、同一时间,不允许既有写的操作,又有读的操作

上面的场景就是典型的 “多读单写”,经常用于文件等数据的读写操作,iOS中的实现方案有:

1、pthread_rwlock:读写锁

2、dispatch_barrier_async:异步栅栏调用

2.7.1、pthread_rwlock_t - 读写锁

读写锁:是计算机程序并发控制的一种同步机制,用于解决读写问题。

读写锁允许并行读、串行写。与互斥锁的一次只有一个线程执行操作相比,性能更高。比如构建缓存系统,将网络资源写入缓存,后期从缓存读取资源。缓存系统必须线程安全,允许并行读取,串行写入(又称多读单写)。


    // 初始化锁

    pthread_rwlock_t lock;

    pthread_rwlock_init(&lock, NULL);

    

    // 读 - 加锁

    pthread_rwlock_rdlock(&lock);

    // 读 - 尝试加锁

    pthread_rwlock_tryrdlock(&lock);

    



    // 写 - 加锁

    pthread_rwlock_wrlock(&lock);

    // 写 - 尝试加锁

    pthread_rwlock_trywrlock(&lock);

    

    // 解锁

    pthread_rwlock_unlock(&lock);

    // 毁锁

    pthread_rwlock_destroy(&lock);



给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值