iOS 之 线程锁

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

@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  {
@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  {
@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  {
@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  {
@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.png

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

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

你可能感兴趣的:(iOS 之 线程锁)