iOS 线程锁

作者: 赵_df7e | 来源:发表于2020-05-12 11:41 被阅读0次

    一般情况下,我们定义属性的时候都是这样定义的:

    @property (nonatomic, copy) NSString *string1;
    @property (nonatomic, strong) NSMutableString *string2;
    
    

    copystrong的区别就不在这里多说了,主要来看下这个nonatomic以及atomic

    nonatomic & atomic

    atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。这里是指系统自动生成的 getter/setter 方法。如果自己重写 getter/setter方法,那atomic/nonatomic只起提示作用

    • atomic:提供多线程安全
      设置成员变量的@property属性时,默认为atomic,提供多线程安全。相当于函数头尾加了锁一样,可以保证数据的完整性。而这种机制是耗费系统资源的。
    {lock}
    if (property != newValue) { 
        [property release]; 
        property = [newValue retain]; 
    }
    {unlock}
    
    
    • nonatomic:禁止多线程,变量保护,提高性能。
      一般iOS程序中,所有属性都声明为nonatomic。这样做的原因是:在iOS中使用同步锁的开销比较大, 这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才醒。

    于是引出了本篇的话题:线程锁

    线程锁

    多线程编程中,应该尽量避免资源在线程之间共享,以减少线程间的相互作用。 但是总是有多个线程相互干扰的情况(如多个线程访问一个资源)。在线程必须交互的情况下,就需要一些同步工具,来确保当它们交互的时候是安全的。

    简单来说:我们引入锁的目的是为了线程安全。

    image

    找到一张关于线程锁性能的比较图片,如图所示

    性能最好的是OSSpinLock(据说是不安全的的,详情请看ibireme大神的不再安全的 OSSpinLock)

    我们一起来看下在iOS开发中常用的几种锁

    1. @synchronized

    推荐文章:
    Peak大神正确使用多线程同步锁@synchronized()
    关于 @synchronized,这儿比你想知道的还要多
    @synchronized 结构所做的事情跟锁(lock)类似:它防止不同的线程同时执行同一段代码。@synchronized是几种iOS多线程同步机制中最慢的一个,同时也是最方便的一个。苹果建立@synchronized的初衷就是方便开发者快速的实现代码同步,语法如下:

    @synchronized(object) {
      //code
    }
    
    

    Peak大神在文章中提醒我们:

    • 慎用@synchronized(self)
      synchronized中传入的object的内存地址,被用作key,通过hash map对应的一个系统维护的递归锁。不管是传入什么类型的object,只要是有内存地址,就能启动同步代码块的效果。
    • 粒度控制,不同的数据使用不同的锁,尽量将粒度控制在最细的程度
      有些人说@synchronized慢,但@synchronized之所以慢是更多的因为没有做好粒度控制。锁本质上是为了让我们的一段代码获得原子性,不同的critical section要使用不同的锁。

    @synchronized也经常用来实现单例,用的更多的还是GCD中的一次函数dispatch_once,关于@synchronized和dispatch_once的性能比较,有兴趣的童鞋们可以看这里

    2. NS系列锁

    NS系列锁指的是NSLockNSConditionNSConditionLockNSRecursiveLock,之所以把这几个放在一起,是因为它们都遵守NSLocking协议,就俩方法,加锁解锁,so easy!

    @protocol NSLocking
    - (void)lock;
    - (void)unlock;
    @end
    
    
    2.1 NSLock 线程锁

    看下NSLock 的API,嗯,很少也很简单:

    @interface NSLock : NSObject <NSLocking> {
    @private
        void *_priv;
    }
    - (BOOL)tryLock;
    - (BOOL)lockBeforeDate:(NSDate *)limit;
    
    @property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    
    @end
    
    

    方法说明:
    trylock:能加锁返回 YES 并执行加锁操作,相当于 lock,反之返回 NO
    lockBeforeDate:这个方法表示会在传入的时间内尝试加锁,若能加锁则执行加锁操作并返回 YES,反之返回 NO。

    2.2 NSConditionLock 条件锁

    condition:条件,顾名思义NSConditionLock就是有条件的加锁,继续来看API

    @interface NSConditionLock : NSObject <NSLocking> {
    @private
        void *_priv;
    }
    
    - (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
    
    @property (readonly) NSInteger condition;
    - (void)lockWhenCondition:(NSInteger)condition;
    - (BOOL)tryLock;
    - (BOOL)tryLockWhenCondition:(NSInteger)condition;
    - (void)unlockWithCondition:(NSInteger)condition;
    - (BOOL)lockBeforeDate:(NSDate *)limit;
    - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
    
    @property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    
    @end
    
    

    比较了一下,貌似跟NSLock差不多,只是加了个condition,还是NSInteger类型的,感觉只要把这个参数搞明白就差不多了。
    condition我们可以理解为一个条件标示,看下创建方法:initWithCondition:创建的时候传入一个条件标识,之后如果使用创建好的锁,必须传入对应的标识才能完成对应的lock和unlock操作

    2.3 NSRecursiveLock 递归锁
    @interface NSRecursiveLock : NSObject <NSLocking> {
    @private
        void *_priv;
    }
    
    - (BOOL)tryLock;
    - (BOOL)lockBeforeDate:(NSDate *)limit;
    
    @property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    
    @end
    
    
    2.4 NSCondition 断言
    @interface NSCondition : NSObject <NSLocking> {
    @private
        void *_priv;
    }
    
    - (void)wait;
    - (BOOL)waitUntilDate:(NSDate *)limit;
    - (void)signal;
    - (void)broadcast;
    
    @property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    
    @end
    
    

    3. dispatch_semaphore 信号量实现加锁(GCD)

    之前写过一篇dispatch_semaphore信号量的文章,麻烦大家手动移驾过去。

    我自己测试的线程锁的性能如下:

    image

    推荐文章(排名不分先后):
    (ibireme大神的不再安全的 OSSpinLock)
    https://www.jianshu.com/p/1e59f0970bf5

    关于递归锁与非递归锁,平常接触比较少,有兴趣的童鞋可以了解一下:https://blog.csdn.net/zouxinfox/article/details/5838861

    原文链接:https://www.jianshu.com/p/b5c63d7c36da

    相关文章

      网友评论

        本文标题:iOS 线程锁

        本文链接:https://www.haomeiwen.com/subject/fsfbnhtx.html