iOS中的锁

作者: LiLS | 来源:发表于2017-11-23 15:54 被阅读137次

    锁是一种同步机制,用于多线程环境中对资源访问的限制
    iOS中常见锁的性能对比图(摘自:ibireme):

    lock_benchmark.png

    iOS锁的介绍及使用
    1.synchronized
    代码:
    -(void)testSynchronized{
    NSObject *obj = [NSObject new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(obj){
    NSLog(@"线程一开始");
    sleep(3);
    NSLog(@"线程一结束");
    }
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(obj){
    NSLog(@"线程二开始");
    }
    });
    }
    执行结果:
    2017-11-22 10:57:19.691200+0800 LiLLock[19850:2037312] 线程一开始
    2017-11-22 10:57:22.695597+0800 LiLLock[19850:2037312] 线程一结束
    2017-11-22 10:57:22.695803+0800 LiLLock[19850:2037311] 线程二开始
    @synchronized(obj)指令使用的obj作为该锁的唯一标识,只有当标识相同时,才能满足互斥条件,如果线程二使用self作为标识,那么线程二就不会阻塞。@synchronized()指令的优点是我们不需要显示创建锁对象,便可以方便实现锁机制。但作为一种防护措施,@synchronized块会隐式添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时自动释放互斥锁。如果不想隐式的异常处理例程消耗额外的资源,可以使用对象锁。
    @synchronized(obj){}内部obj被释放或者被置为nil不会影响锁的功能,如果obj一开始就被置为nil,那么就会丢失锁的功能
    2.NSLock
    从NSLock类的.h文件中可以看出,NSLock、NSConditionLock、NSRecursiveLock、NSCondition都遵循NSLocking 协议:
    NSLock实现了最基础的互斥锁,通过lock、unlock来加锁和解锁
    代码:
    -(void)testNSLock{
    NSLock *lock = [NSLock new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    NSLog(@"线程1加锁成功");
    sleep(3);
    [lock unlock];
    NSLog(@"线程1解锁成功");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    NSLog(@"线程2加锁成功");
    sleep(2);
    [lock unlock];
    NSLog(@"线程2解锁成功");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    if ([lock tryLock]) {
    NSLog(@"线程3加锁成功");
    [lock unlock];
    NSLog(@"线程3解锁成功");
    }else{
    NSLog(@"线程3加锁失败");
    }
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(6);
    if ([lock tryLock]) {
    NSLog(@"线程4加锁成功");
    [lock unlock];
    NSLog(@"线程4解锁成功");
    }else{
    NSLog(@"线程4加锁失败");
    }
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:7]]) {
    NSLog(@"线程5加锁成功");
    [lock unlock];
    NSLog(@"线程5解锁成功");
    }else{
    NSLog(@"线程5加锁失败");
    }
    });

    }
    执行结果:
    2017-11-22 15:11:41.927360+0800 LiLLock[20818:2216630] 线程1加锁成功
    2017-11-22 15:11:42.929336+0800 LiLLock[20818:2216631] 线程3加锁失败
    2017-11-22 15:11:44.930266+0800 LiLLock[20818:2216629] 线程2加锁成功
    2017-11-22 15:11:44.930284+0800 LiLLock[20818:2216630] 线程1解锁成功
    2017-11-22 15:11:46.934496+0800 LiLLock[20818:2216629] 线程2解锁成功
    2017-11-22 15:11:46.934548+0800 LiLLock[20818:2216632] 线程5加锁成功
    2017-11-22 15:11:46.934718+0800 LiLLock[20818:2216632] 线程5解锁成功
    2017-11-22 15:11:47.929784+0800 LiLLock[20818:2216633] 线程4加锁成功
    2017-11-22 15:11:47.929987+0800 LiLLock[20818:2216633] 线程4解锁成功
    综上总结:
    1.除了lock和unlock方法外,还提供了tryLock和lockBeforeDate:两个方法
    2.tryLock不会阻塞线程,tryLock能加锁返回YES,不能加锁返回NO,都会执行后续代码
    3.lockBeforeDate:会在指定时间之前尝试加锁,会阻塞线程,如果在指定时间之前能加锁返回YES,在指定时间之前都不能加锁返回NO
    4.由于是互斥锁,当一个线程进行访问的时候,该线程获取到锁,其他线程进行访问的时候,被系统挂起,直到该线程释放锁,其他线程才能对其进行访问,从而确保线程安全。如果连续锁定两次,则会造成死锁问题。
    3.NSRecursiveLock
    NSRecursiveLock递归锁,可以被一个线程多次获得而不会造成死锁,它记录了成功获得锁的次数,每一次成功获得锁必须有一个解锁的操作与其对应,这样才不会造成死锁。NSRecursiveLock记录了加锁和解锁的次数,当二者平衡是,才能完全释放锁,被其他线程获取。
    代码:
    -(void)testRecursiveLock{
    NSRecursiveLock *lock = [NSRecursiveLock new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    static void(^block)(int index);
    block = ^(int index){
    [lock lock];
    NSLog(@"%d加锁成功",index);
    if (index >0) {
    block(index-1);
    }
    [lock unlock];
    NSLog(@"%d解锁成功",index);
    };
    block(2);
    });
    }
    执行结果:
    2017-11-22 17:29:45.886354+0800 LiLLock[21169:2298294] 2加锁成功
    2017-11-22 17:29:45.886558+0800 LiLLock[21169:2298294] 1加锁成功
    2017-11-22 17:29:45.886654+0800 LiLLock[21169:2298294] 0加锁成功
    2017-11-22 17:29:45.886747+0800 LiLLock[21169:2298294] 0解锁成功
    2017-11-22 17:29:45.886850+0800 LiLLock[21169:2298294] 1解锁成功
    2017-11-22 17:29:45.887872+0800 LiLLock[21169:2298294] 2解锁成功
    综上总结:
    如果用NSLock来加锁,lock先上了锁,未解锁就进入到递归的下一层,再次请求加锁,线程就会被阻塞,阻塞了线程后面的d代码就不会被执行,就不能解锁,一直阻塞线程,造成死锁。在这种情况下,递归锁就能很好的解决这些问题。
    4.NSConditionLock
    NSConditionLock对象所定义的互斥锁可以在某些条件下进行加锁和解锁,和NSLock类似,也遵循NSLocking协议,方法也类似,多了condition属性,以及每个操作都多了一个关于condition属性的方法,如:tryLock、tryLockWhenCondition:,所以NSConditionLock可以称作条件锁。
    注意:只有当condition参数和初始化condition相同时,才能加锁成功;unlockWithCondition:并不是满足条件时才会解锁,而是解锁成功后修改condition的值。
    代码:
    -(void)testNSConditionLock{
    NSConditionLock *lock = [[NSConditionLock alloc]initWithCondition:2];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lock];
    NSLog(@"线程1加锁成功");
    sleep(2);
    [lock unlockWithCondition:3];
    NSLog(@"线程1解锁成功");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [lock lockWhenCondition:2];
    NSLog(@"线程2加锁成功");
    [lock unlockWithCondition:3];
    NSLog(@"线程2解锁成功");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    if ([lock tryLockWhenCondition:2]) {
    NSLog(@"线程3加锁成功");
    sleep(1);
    [lock lockWhenCondition:3];
    NSLog(@"线程3解锁成功");
    }else{
    NSLog(@"线程3加锁失败");
    }

    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock lockWhenCondition:3 beforeDate:[NSDate dateWithTimeIntervalSinceNow:5] ]) {
            NSLog(@"线程4加锁成功");
            sleep(1);
            [lock unlockWithCondition:2];
            NSLog(@"线程4解锁成功");
        }else{
            NSLog(@"线程4加锁失败");
        }
        
    });
    

    }
    执行结果:
    2017-11-22 18:37:26.109882+0800 LiLLock[21371:2343295] 线程3加锁失败
    2017-11-22 18:37:26.109882+0800 LiLLock[21371:2343299] 线程1加锁成功
    2017-11-22 18:37:28.111479+0800 LiLLock[21371:2343299] 线程1解锁成功
    2017-11-22 18:37:28.111479+0800 LiLLock[21371:2343296] 线程4加锁成功
    2017-11-22 18:37:29.116467+0800 LiLLock[21371:2343296] 线程4解锁成功
    2017-11-22 18:37:29.116467+0800 LiLLock[21371:2343298] 线程2加锁成功
    2017-11-22 18:37:29.116664+0800 LiLLock[21371:2343298] 线程2解锁成功
    综上总结:
    部分相同方法见NSLock总结,lockWhenCondition:当condition条件满足时阻塞线程,不满足条件时线性不进行任何操作;tryLockWhenCondition:当condition条件满足会尝试加锁,不会阻塞线程,成功返回YES,失败返回NO,都会执行各自的后续操作;lockWhenCondition: beforeDate: 在指定的时间之前尝试加锁,会阻塞线程,如果在指定时间之前满足condition条件加锁成功返回YES,超过指定时间返回NO,执行后续操作。
    5.NSCondition
    NScondition是一种特殊类型的锁,通过它可以实现不同线程的调度。一个线程被某一个条件所阻塞,直到另一个线程满足该条件从而发信号给该线程使该线程可以正常运行。例如有一个下载图片的线程,一个处理图片的线程,当没有图片处理时处理图片的线程就会阻塞,当下载图片的线程下载完图片时满足了处理图片线程的需求,这个时候下载线程给处理线程一个信号,处理线程就会恢复运行。
    注意:NSCondition对象实际上是一个锁和一个线程检查器,锁上之后其他线程也可以上锁,而之后根据条件决定是否继续运行线程,即线程是否要进入waiting状态。如果进入waiting状态,其他线程的该锁执行signal或者broadcast方法,线程被唤起,继续运行。NSCondition可以手动设置挂起和唤醒,据此可以设置线程依赖。
    代码:
    -(void)testNSCondition{
    NSCondition *condition = [NSCondition new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [condition lock];
    NSLog(@"线程1加锁成功");
    [condition wait];
    NSLog(@"线程1被唤醒");
    [condition unlock];
    NSLog(@"线程1解锁成功");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [condition lock];
    NSLog(@"线程2加锁成功");
    sleep(2);
    if ([condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
    NSLog(@"线程2被唤醒");
    }
    [condition unlock];
    NSLog(@"线程2解锁成功");

    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"线程3加锁成功");
        [condition wait];
        NSLog(@"线程3被唤醒");
        [condition unlock];
        NSLog(@"线程3解锁成功");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"线程4加锁成功");
        sleep(5);
        [condition wait];
        NSLog(@"线程4被唤醒后不解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"线程5加锁成功");
        sleep(7);
        [condition wait];
        NSLog(@"线程5被醒后不解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        [condition signal];
        sleep(3);
        [condition broadcast];
        sleep(6);
        [condition signal];
        sleep(8);
        [condition signal];
    });
    

    }
    执行结果:
    2017-11-23 11:13:47.710664+0800 LiLLock[22731:2495287] 线程1加锁成功
    2017-11-23 11:13:47.711805+0800 LiLLock[22731:2495289] 线程2加锁成功
    2017-11-23 11:13:49.715733+0800 LiLLock[22731:2495290] 线程3加锁成功
    2017-11-23 11:13:49.716027+0800 LiLLock[22731:2495314] 线程4加锁成功
    2017-11-23 11:13:54.719161+0800 LiLLock[22731:2495430] 线程5加锁成功
    2017-11-23 11:14:01.722348+0800 LiLLock[22731:2495287] 线程1被唤醒
    2017-11-23 11:14:01.722513+0800 LiLLock[22731:2495287] 线程1解锁成功
    2017-11-23 11:14:01.722535+0800 LiLLock[22731:2495290] 线程3被唤醒
    2017-11-23 11:14:01.722677+0800 LiLLock[22731:2495290] 线程3解锁成功
    2017-11-23 11:14:01.722697+0800 LiLLock[22731:2495289] 线程2被唤醒
    2017-11-23 11:14:01.722774+0800 LiLLock[22731:2495289] 线程2解锁成功
    2017-11-23 11:14:01.722783+0800 LiLLock[22731:2495314] 线程4被唤醒后不解锁
    综上总结:
    1.加上锁之后,调用条件对象的wait或者waitUnitilDate:来阻塞线程,直到条件对象发出信号或者超时才会执行后续操作。
    2.signal和broadcast的区别是:signal是一个信号量,只能唤起一个等待的线程,要想唤起多个线程必须多次调用,而broadcast可以唤起所有等待的线程。
    3.signal或者broadcast要想唤起线程必须要有唤醒对象解锁,否则无法唤起,线程被唤起后必须有解锁操作,否则之后无法唤起其他线程,其他线程也无法加锁。
    6.dispatch_semaphore
    dispatch_semaphore使用信号量机制实现锁,等待信号和发送信号。
    dispatch_semaphore是GCD用于同步的一种方式,与他相关的方法有三个,一个是创建信号,一个是等待信号,一个是发送信号。
    dispatch_semaphore的机制就是当有多个线程进行访问的时候,只要有一个获得信号,其他的线程就必须等待该信号释放。
    代码:
    -(void)testDispatch_semaphore{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_time_t overtime = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC6);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, overtime);
    NSLog(@"线程1开始");
    sleep(3);
    NSLog(@"线程1结束");
    dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, overtime);
    NSLog(@"线程2开始");
    sleep(5);
    NSLog(@"线程2结束");
    dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, overtime);
    NSLog(@"线程3开始");
    sleep(1);
    NSLog(@"线程3结束");
    dispatch_semaphore_signal(semaphore);
    });
    }
    执行结果:
    -(void)testDispatch_semaphore{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_time_t overtime = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC
    4);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, overtime);
    NSLog(@"线程1开始");
    sleep(3);
    NSLog(@"线程1结束");
    dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    dispatch_semaphore_wait(semaphore, overtime);
    NSLog(@"线程2开始");
    sleep(5);
    NSLog(@"线程2结束");
    dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(2);
    dispatch_semaphore_wait(semaphore, overtime);
    NSLog(@"线程3开始");
    NSLog(@"线程3结束");
    dispatch_semaphore_signal(semaphore);
    });
    }
    综上总结:
    1.dispatch_semaphore和condition类似,都是一种基于信号的同步方式,但是NSCondition只能发送,不能保存(如果没有线程在等待,则发送的信号失效)。而dispatch_semaphore能保存发送的信号。dispatch_semaphore的核心是dispatch——semaphore_t类型的信号量;
    2.dispatch_semaphore_create(1)方法可以创建一个dispatch_semaphore_t类型的信号量,设定信号量的初始值为1。注意这里的传入参数必须大于或者等于0,否则dispatch_semaphore_create会返回NULL。
    3.dispatch_semaphore_wait(semaphore,overTime);方法会判断semaphore的信号值是否大于0.大于0不会阻塞线程,消耗掉一个信号,执行后续任务。如果信号量为0,该线程会和NScondition一样直接进入waiting状态,等待其他线程发送信号唤醒线程去执行后续任务,或者当overTime时限到了,也会执行后续任务。
    4.dispatch_semaphore_signal(semaphore);发送信号,如果没有等待的线程接受信号,则使signal信号值加一(做到对信号的保存)。
    5.一个dispatch_semaphore_wait(semaphore,overTime);方法会去对应一个dispatch_semaphore_singal(semaphore);看起来像NSLock的lock和unlock,其实可以这样理解,区别只在于信号量这个参数,lock unlcok只能同意时间,一个线程访问被保护的临界区,而如果dispatch_semaphore的信号量的初始值为x,则可以有x个线程同时访问被保护的临界区。
    7.pthread_mutex和pthread_mutex(recursive)
    pthread标识POSIX thread,定义了一组跨平台的线程相关的API,POSIX互斥锁是一种超级易用的互斥锁,使用的时候:
    1.只需要使用pthread_mutex_init痴实话一个pthread_mutex_t,
    2.pthread_mutex_lock或者pthread_mutex_trylock来锁定,
    3pthread_mutex_unlcok来解锁,
    4.当使用完成后,记得调用pthread_mutex_destory来销毁。
    代码:
    -(void)testPthread_mutex{
    __block pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,NULL);
    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^{
    pthread_mutex_lock(&mutex);
    NSLog(@"线程1开始");
    sleep(3);
    pthread_mutex_unlock(&mutex);
    NSLog(@"线程1结束");
    });
    dispatch_async(concurrentQueue, ^{
    sleep(1);
    pthread_mutex_lock(&mutex);
    NSLog(@"线程2开始");
    pthread_mutex_unlock(&mutex);
    NSLog(@"线程2结束");
    });
    __block pthread_mutex_t mutex1;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&mutex1, &attr);
    pthread_mutexattr_destroy(&attr);
    dispatch_barrier_async(concurrentQueue, ^{
    static void(^block)(int index);
    block = ^(int index){
    pthread_mutex_lock(&mutex1);
    NSLog(@"线程3递归%d开始",index);
    if (index > 0) {
    block(index-1);
    }
    pthread_mutex_unlock(&mutex1);
    NSLog(@"线程3递归%d结束",index);
    };
    block(3);
    });

    }
    执行结果:
    2017-11-23 15:01:00.738222+0800 LiLLock[23523:2663942] 线程1开始
    2017-11-23 15:01:03.741337+0800 LiLLock[23523:2663941] 线程2开始
    2017-11-23 15:01:03.741337+0800 LiLLock[23523:2663942] 线程1结束
    2017-11-23 15:01:03.741527+0800 LiLLock[23523:2663941] 线程2结束
    2017-11-23 15:01:03.741647+0800 LiLLock[23523:2663941] 线程3递归3开始
    2017-11-23 15:01:03.741744+0800 LiLLock[23523:2663941] 线程3递归2开始
    2017-11-23 15:01:03.741842+0800 LiLLock[23523:2663941] 线程3递归1开始
    2017-11-23 15:01:03.742096+0800 LiLLock[23523:2663941] 线程3递归0开始
    2017-11-23 15:01:03.742195+0800 LiLLock[23523:2663941] 线程3递归0结束
    2017-11-23 15:01:03.742291+0800 LiLLock[23523:2663941] 线程3递归1结束
    2017-11-23 15:01:03.742528+0800 LiLLock[23523:2663941] 线程3递归2结束
    2017-11-23 15:01:03.742627+0800 LiLLock[23523:2663941] 线程3递归3结束
    综上总结:
    1.它的用法和NSLock芙蓉lock unlcok用法一致,而它也有一个pthread_mutex_trylock方法,pthread_mutex_trylock和tryLock芙蓉区别在于,tryLcok返回的是YES和NO,pthread_mutex_trylock加锁成功返回的是0,失败返回的是错误提示码。
    2.pthread_mutex(&recursive)作用和NSRecursiveLock递归锁类似,如果使用pthread_mutex_init(&theLock,NULL);初始化锁的画,上面的代码的第二部分就会出现死锁现象,使用递归锁就可以避免这种现象。
    8.OSSpinLock
    OSSpinLock是一种自旋锁,和互斥锁类似,都是为了保证线程安全的锁。大门二者的区别是不一样的,对于互斥锁,当一个线程获得这个锁之后,其他想要获得所的线程将会被阻塞,直到该锁被释放。但是自旋锁不一样,当一个线程获得锁之后,其他线程将会一直循环在哪里查看该锁是否被释放,所以此锁比较适用于锁的持有者保存时间较短的情况下。
    代码:
    -(void)testOSSpinLock{
    __block OSSpinLock theLock = OS_SPINLOCK_INIT;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    OSSpinLockLock(&theLock);
    NSLog(@"线程1开始");
    sleep(3);
    OSSpinLockUnlock(&theLock);
    NSLog(@"线程1结束");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    OSSpinLockLock(&theLock);
    NSLog(@"线程2开始");
    OSSpinLockUnlock(&theLock);
    NSLog(@"线程2结束");
    });
    }
    执行结果:
    2017-11-23 15:25:40.096050+0800 LiLLock[23666:2693366] 线程1开始
    2017-11-23 15:25:43.096610+0800 LiLLock[23666:2693366] 线程1结束
    2017-11-23 15:25:43.153535+0800 LiLLock[23666:2693364] 线程2开始
    2017-11-23 15:25:43.154364+0800 LiLLock[23666:2693364] 线程2结束
    注:YY@ibireme的文章阐述了自旋锁存在优先级反转问题,具体文章可以点击此处而OSSpinLock在iOS10.0中被<os/lock.h>中的os_unfair_lock取代了。
    9.os_unfair_lock
    这个锁就是OSSpinLock的替代,用来解决优先级反转问题。
    代码:
    -(void)testOs_unfair_lock{
    __block os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    os_unfair_lock_lock(&unfairLock);
    NSLog(@"线程1开始");
    sleep(3);
    os_unfair_lock_unlock(&unfairLock);
    NSLog(@"线程1结束");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    sleep(1);
    os_unfair_lock_lock(&unfairLock);
    NSLog(@"线程2开始");
    os_unfair_lock_unlock(&unfairLock);
    NSLog(@"线程2结束");
    });
    }
    执行结果:
    2017-11-23 15:46:16.346936+0800 LiLLock[23897:2720025] 线程1开始
    2017-11-23 15:46:19.418207+0800 LiLLock[23897:2720021] 线程2开始
    2017-11-23 15:46:19.418245+0800 LiLLock[23897:2720025] 线程1结束
    2017-11-23 15:46:19.418392+0800 LiLLock[23897:2720021] 线程2结束
    总结:
    1.每一种锁的执行步骤基本都是加锁、等待、解锁。
    2.@synchronized()执行效率最低,但是使用起来方便,如果没有什么性能瓶颈可以使用。
    3.OSSpinLock的效率最高,但是存在优先级反转问题不安全,iOS 10之后出了os_unfair_lock来代替OSSpinLock,dispatch_semaphore和pthread_mutex的执行效率紧随其后,既可以保证效率又可以保证安全是比较好的选择。
    4.遵循NSLocking的锁,执行效率为NSLock>NSCondition>NSRecursiveLock>NSConditionLock

    相关文章

      网友评论

        本文标题:iOS中的锁

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