常见的锁,特别注意点

50 阅读4分钟

blog.csdn.net/banbaodevel…

锁是线程编程同步工具的基础。iOS开发中常用的锁有如下几种:

1、@synchronized

2、NSLock 对象锁

3、NSRecursiveLock 递归锁

4、NSConditionLock 条件锁

5、pthread_mutex 互斥锁(C语言)

6、dispatch_semaphore 信号量实现加锁(GCD)

7、 OSSpinLock (暂不建议使用)

比较说明:

@synchronized 关键字加锁 互斥锁,性能较差不推荐使用


@synchronized(这里添加一个OC对象,一般使用self) {

       这里写要加锁的代码

  }

 注意点

   1.加锁的代码尽量少

   2.添加的OC对象必须在多个线程中都是同一对象

        3.优点是不需要显式的创建锁对象,便可以实现锁的机制。

       4. @synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。


NSLock 互斥锁 不能多次调用 lock方法,会造成死锁


NSRecursiveLock 递归锁

使用锁最容易犯的一个错误就是在递归或循环中造成死锁

如下代码中,因为在线程1中的递归block中,锁会被多次的lock,所以自己也被阻塞

NSRecursiveLock类定义的锁可以在同一线程多次lock,而不会造成死锁。

递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操作。

只有所有的锁住和解锁操作都平衡的时候,锁才真正被释放给其他线程获得。

NSConditionLock 条件锁

条件锁,一个线程获得了锁,其它线程等待。

[xxxx lock]; 

表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执行此行以下代码,如果已经有其他线程获得锁(可能是条件锁,或者无条件锁),则等待,直至其他线程解锁

[xxx lockWhenCondition:A条件]; 

表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直至它解锁。

[xxx unlockWithCondition:A条件]; 

表示释放锁,同时把内部的condition设置为A条件

dispatch_semaphore 信号量实现加锁

GCD中也已经提供了一种信号机制,使用它我们也可以来构建一把”锁”(从本质意义上讲,信号量与锁是有区别。):

线程和锁


经典案例:

1.- (void)viewDidLoad {

    // 异步主队列

    dispatch_async(global_queue,^{

        NSLog(@"1");

        [self performSelector: @selector(printLog) withObject: nil, afterDelay: 0];

        NSLog(@"3");

    });

}

  • (void)printLog { NSLog(@"2");}

分析:输出结果为13。

global_queue是全局队列,采用dispatch_async,会开辟一个子线程,实际上任务会在GCD底层所维护的线程池当中某个线程中执行处理。子线程的runloop默认是不开启的,而

通过异步方式分派任务到全局并发队列后,会在GCD底层所维护的线程池当中某个线程中执行处理,在GCD底层所维护的线程池中的线程默认不会开启对应的runloop,而performSelector:withObject:afterDelay是在没有runloop的情况下会失效,所以此方法不执行。

2.怎样利用GCD实现多读单写?

分析:需要满足读者和读者并发、读者和写者互斥、写者和写者互斥。

1.读处理之间需要并发的,用到并发队列,因为读取操作,往往需要立刻返回结果,故采用同步。这些读处理允许在对个子线程。2.写处理时其它操作都不能执行。利用栅栏函数异步操作,原因是栅栏函数同步操作会阻塞当前线程,如果当前线程还有其它操作,就会影响用户体验。

多读单写方案:利用GCD提供的栅栏函数。

dispatch_barrier_async(concurrent_queue,^{ //写操作 });

juejin.cn/post/713490…