iOS底层原理篇(十四) ---- NSLock、NSRecursiveLock、@synchronize比较

  • 首先看一个crash
	- (void)viewDidLoad {
    	[super viewDidLoad];
    	NSLog(@"123");
    	[self wm_crash];
	}

	- (void)wm_crash{
    	for (int i = 0; i < 200000; i++) {
        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
        	    _testArray = [NSMutableArray array];
        	});
    	}
	}
	//Thread 2: EXC_BAD_ACCESS (code=1, address=0x564e3f616598)
	// sidetable_release 报错
	// 因为旧值release时,会存在多个同时release的情况 
  • 上篇@synchronize我们已经研究过,现在我们上面的代码加锁!
	- (void)wm_crash{
    	for (int i = 0; i < 200000; i++) {
        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
            	@synchronized (_testArray) {
                	_testArray = [NSMutableArray array];
            	}
        	});
    	}
	}
	//按照上面处理之后,也是crash,报同样的错误!
	//再做处理如下:
	- (void)wm_crash{
    	for (int i = 0; i < 200000; i++) {
        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
            	@synchronized (self) {
               	_testArray = [NSMutableArray array];
            	}
        	});
    	}
	}
	//运行正常了
  • 这里需要说一下:我们说过,@synchronize当加锁对象为nil时,do nothing不做任何处理,我们对_testArray加锁时,这里_testArray是会为nil的,导致加锁失败!出现异常!!!但是使用self时,self一定部位nil,所以不会出现异常!
    iOS底层原理篇(十四) ---- NSLock、NSRecursiveLock、@synchronize比较_第1张图片
  • 但是这里我想说,synchronize的效率比较低,而且此处我们有更优解!
  • NSLock 互斥锁
	- (void)wm_crash{
    	NSLock *lock = [[NSLock alloc] init];
    	for (int i = 0; i < 200000; i++) {
        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
            	[lock lock];
            	_testArray = [NSMutableArray array];
            	[lock unlock];
        	});
    	}
	}
	//可以解决问题
  • NSLock在Foundation框架,swift的有开源,这里去看一下swift的底层源码!
	public override init() {
	#if os(Windows)
        	InitializeSRWLock(mutex)
        	InitializeConditionVariable(timeoutCond)
        	InitializeSRWLock(timeoutMutex)
	#else
        	pthread_mutex_init(mutex, nil)
	#if os(macOS) || os(iOS) //iOS
        	pthread_cond_init(timeoutCond, nil)
        	pthread_mutex_init(timeoutMutex, nil)
	#endif
	#endif
    }
    //顺便把NSRecursiveLock递归锁源码也拿过来:
    public override init() {
    	super.init()
	#if os(Windows)
        InitializeCriticalSection(mutex)
        InitializeConditionVariable(timeoutCond)
        InitializeSRWLock(timeoutMutex)
	#else
	#if CYGWIN
        var attrib : pthread_mutexattr_t? = nil
	#else
        var attrib = pthread_mutexattr_t()
	#endif
        withUnsafeMutablePointer(to: &attrib) { attrs in
            pthread_mutexattr_init(attrs)
            //PTHREAD_MUTEX_RECURSIVE递归锁标记
            pthread_mutexattr_settype(attrs,Int32(PTHREAD_MUTEX_RECURSIVE))
            
            pthread_mutex_init(mutex, attrs)
        }
	#if os(macOS) || os(iOS)
        pthread_cond_init(timeoutCond, nil)
        pthread_mutex_init(timeoutMutex, nil)
	#endif
	#endif
    }
  • NSRecursiveLock 递归锁
	//下面代码递归调用,我们使用互斥锁搞了一个阻塞的问题!!!注意阻塞与死锁的区别,阻塞没有闭环,死锁形成了相互等待的闭环!
	- (void)wm_testRecursive{
    	NSLock *lock = [[NSLock alloc] init];
    	dispatch_async(dispatch_get_global_queue(0, 0), ^{
        	static void (^testBlock)(int);
        	testBlock = ^(int value){
            	[lock lock];
            	if (value > 0) {
                	NSLog(@"current value = %d",value);
                	testBlock(value - 1);
            	}
            	[lock unlock];
        	};
        	testBlock(100);
    	});
	}
	//这里只会打印current value = 100,然后发生阻塞
	//涉及到递归调用,我们可以使用NSRecursiveLock递归锁来解决
	- (void)wm_testRecursive{
    	NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
    	dispatch_async(dispatch_get_global_queue(0, 0), ^{
        	static void (^testBlock)(int);
        	testBlock = ^(int value){
            	[lock lock];
            	if (value > 0) {
                	NSLog(@"current value = %d",value);
                	testBlock(value - 1);
            	}
            	[lock unlock];
        	};
        	testBlock(100);
    	});
	}
	//运行正常
	//这里只是一个不怎么恰当的例子.....
  • 那上面的递归调用,递归锁解决的代码再改变一下:
	- (void)wm_testRecursive{
    	NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
    	for (int i = 0; i < 100; i++) {
        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
            	static void (^testBlock)(int);
            	testBlock = ^(int value){
                	[lock lock];
                	if (value > 0) {
                    	NSLog(@"current value = %d",value);
                    	testBlock(value - 1);
                	}
                	[lock unlock];
            	};
            	testBlock(10);
        	});
    	}
	}
	//这里就发生了死锁现象!!!
	//这里我们for循环的同时,异步的去递归调用,这时会有很多线程在相互等待操作!lock没有找到合适的出口!
	//这里在来改变一下代码:
	- (void)wm_testRecursive{
    	for (int i = 0; i < 100; i++) {
        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
            	static void (^testBlock)(int);
            	testBlock = ^(int value){
                	@synchronized (self) {
                    	if (value > 0) {
                        	NSLog(@"current value = %d",value);
                        	testBlock(value - 1);
                    	}
                	}
            	};
            	testBlock(10);
        	});
    	}
	}
	//这里正常运行!!!
	//为什么呢?
	//我们知道@synchronized(self)加锁时,是从缓存中去取的,不会重复的去创建!
	//这也是synchronized优于NSRecursiveLock的地方,防止死锁的产生!
  • 所以,我们在使用的时候,看情况来定,当我们是普通线程安全,就使用NSLock就可以了,效率高!但是当我们是递归调用时,就要使用NSRecursiveLock递归锁来保证线程安全!再有就是当我们不仅要使用递归调用,还会有循环的外界线程影响时,就要注意死锁的产生!!!

你可能感兴趣的:(iOS底层原理)