ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

IOS 关于锁

2022-01-11 12:30:00  阅读:200  来源: 互联网

标签:IOS pthread NSLog dispatch 线程 关于 time mutex


前言

为什么需要锁:
多条线程存在同时操作(删、查、读、写)同一个文件or对象or变量。如果不是同时或者不是同一个那就不用加锁了。关键变量:必须是“同时”,“同一事物”

自旋锁OSSpinLock

  • 个人理解:就是一直等,忙等,一直while 到锁被解开,自旋锁不会让等待的线程进入休眠状态
  • 已经废弃,容易出现优先级反转
  • 关于优先级反转:现在有2个高优先级线程A,一个低优先级线程B
    • 情况1:A先获得锁,执行A线程的任务,然后B获取锁,执行B的任务。正常
    • 情况2:B先获得锁,因为B是低优先级线程,这个时候系统会给A分配相对多的系统资源,B能获得少量资源,B的任务先执行,A后执行,正常
    • 情况3:B先获取锁,因为B是低优先级线程,这个时候系统会给A分配相对多的系统资源,没有分配给B资源。这种情况下,B获取了锁,但是没有系统资源,就会一直等待系统资源,造成死锁。
///  自旋锁 需要导入头文件:#import <libkern/OSAtomic.h>
- (void)testSpinLock {
    __block OSSpinLock oslock = OS_SPINLOCK_INIT;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"1准备上锁");
        OSSpinLockLock(&oslock);
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        OSSpinLockUnlock(&oslock);
        NSLog(@"1解锁");
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"2准备上锁");
        OSSpinLockLock(&oslock);
        int time = arc4random() %5;
        NSLog(@"2线程:%d",time);
        sleep(time);
        OSSpinLockUnlock(&oslock);
        NSLog(@"2解锁");
    });
}

不公平锁os_unfair_lock

  • iOS 10 后用来代替spinLock
  • 底层锁,尝试获取锁不会忙等,解锁时由内核唤醒
/// 不公平锁:#import <os/lock.h>
- (void)testUnfailLock {
    static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"1准备上锁");
        os_unfair_lock_lock(&lock);
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        os_unfair_lock_unlock(&lock);
        NSLog(@"1解锁");
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2准备上锁");
        os_unfair_lock_lock(&lock);
        int time = arc4random() %5;
        NSLog(@"2线程:%d",time);
        sleep(time);
        os_unfair_lock_unlock(&lock);
        NSLog(@"2解锁");
    });
}

互斥锁

  • 可移植操作系统接口(Portable Operating System Interface,缩写为 POSIX)
  • 这个是iOS 底层锁,可以实现多种上层锁:递归锁,条件锁等等

pthread_mutex_t

/// 互斥锁 #import <pthread.h>
- (void)testMutexLock {
    static pthread_mutex_t mutex_t;
    pthread_mutex_init(&mutex_t,NULL);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"1准备上锁");
        pthread_mutex_lock(&mutex_t);
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        pthread_mutex_unlock(&mutex_t);
        NSLog(@"1解锁");
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2准备上锁");
        pthread_mutex_lock(&mutex_t);
        int time = arc4random() %5;
        NSLog(@"2线程:%d",time);
        sleep(time);
        pthread_mutex_unlock(&mutex_t);
        NSLog(@"2解锁");
    });
//    pthread_mutex_destroy(&recursiveMutex)
}

NSLock

  • NSLock 是对pthread_mutex 的封装
  • NSLock 不是递归锁,不能在同一线程多次加锁,会造成死锁
- (id) init
 {
   if (nil != (self = [super init]))
   {
       if (0 != pthread_mutex_init(&_mutex, &attr_reporting))
        {
          DESTROY(self);
         }
    }
   return self;
 }

 - (BOOL) lockBeforeDate: (NSDate*)limit
 {
   do{
       int err = pthread_mutex_trylock(&_mutex);
       if (0 == err)
             {
           CHK(Hold)
               return YES;
             }
       if (EDEADLK == err)
             {
               (*_NSLock_error_handler)(self, _cmd, NO, @"deadlock");
             }
       sched_yield();
     } while ([limit timeIntervalSinceNow] > 0);
   return NO;
 }
- (void)testNSLock {
    NSLock *lock = [NSLock new];
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1准备上锁");
        [lock lock];
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        [lock unlock];
        NSLog(@"1解锁");
    });

    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1准备上锁");
        // 尝试上锁,获取资源,如果获取不超过,释放自由,在给定的时间内,一直轮询获取锁,
        BOOL ret =  [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4]];
        if (ret) {
            int time = arc4random() %5;
            NSLog(@"1线程:%d",time);
            [lock unlock];
        }else{
            NSLog(@"失败");
        }
    });
}

递归锁

  • 同一个线程,多次加锁

MutexRecursive

  • 使用 pthread_cond_wait or pthread_cond_timedwait 需要先上锁
  • pthread_cond_t 与 pthread_mutex_t 应该一一对应,不可多个pthread_mutex_t 使用同一个pthread_cond_t
  • wait 时线程处于阻塞态,并被挂起,不占用CPU,知道收到信号被唤醒,重新上锁
/// 递归锁 MutexRecursive
- (void)testMutexRecursive {
    static pthread_mutex_t mutex_t;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&mutex_t, &attr);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        static void (^OneBlock)(int);
        OneBlock = ^(int value) {
            NSLog(@"%d准备上锁",value);
            pthread_mutex_lock(&mutex_t);
            int time = arc4random() %5;
            NSLog(@"%d线程:%d",value,time);
            if(value > 0){
                OneBlock(value - 1);
            }
            sleep(time);
            pthread_mutex_unlock(&mutex_t);
            NSLog(@"%d解锁",value);
        };
        OneBlock(5);
        NSLog(@"执行完成");
        
    });
    
//    pthread_mutex_destroy(&recursiveMutex)
}

NSRecursiveLock

  • NSRecursiveLock 也是 对pthread_mutex 的封装
- (void)testNSRecursiveLock {
    NSRecursiveLock *recursiveLock = [NSRecursiveLock new];
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        static void (^OneBlock)(int);
        OneBlock = ^(int value) {
            NSLog(@"%d准备上锁",value);
            [recursiveLock lock];
            int time = arc4random() %5;
            NSLog(@"%d线程:%d",value,time);
            if(value > 0){
                OneBlock(value - 1);
            }
            sleep(time);
            [recursiveLock unlock];
            NSLog(@"%d解锁",value);
        };
        OneBlock(5);
        NSLog(@"执行完成");
        
    });
}

条件锁

pthread_cond

- (void)testMutexCond {
    static pthread_mutex_t mutex_t;
    static pthread_cond_t cond_t;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&mutex_t, &attr);
    
    pthread_cond_init(&cond_t,NULL);
    
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1准备上锁");
        pthread_mutex_lock(&mutex_t);
        pthread_cond_wait(&cond_t, &mutex_t);
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        pthread_mutex_unlock(&mutex_t);
        NSLog(@"1解锁");
    });
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2准备上锁");
        pthread_mutex_lock(&mutex_t);
        
        struct timespec outtime;
        outtime.tv_sec = time(NULL) + 5;
        outtime.tv_nsec = 0;

        pthread_cond_timedwait(&cond_t, &mutex_t, &outtime);
        int time = arc4random() %5;
        NSLog(@"2线程:%d",time);
        sleep(time);
        pthread_mutex_unlock(&mutex_t);
        NSLog(@"2解锁");
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"发送了一个信号");
        pthread_cond_signal(&cond_t);
        // 广播一个信号
//        pthread_cond_broadcast(&cond_t);
    });
//    pthread_mutex_destroy(&mutex_t);
//    pthread_cond_destroy(&cond);
//    pthread_mutexattr_destroy(&attr);
}

NSCondition

  • NSCondition 是对 pthread_mutex_t 和 pthread_cond_t 的封装 (GNUstep 的实现方式)
  • 条件锁,通过信号来控制
  • lock 上锁 unlock 解锁
  • wait / waitUntilDate 等待一个信号,或者超时,等待的时候线程休眠,会暂时解锁,这个时候其他线程可以上锁,等待结束后,重新自动上锁,
  • signal 发送一个信号,如果有多个线程中都在等待,只有一个线程会接收到
  • broadcast 广播一个信号,如果有多个线程中都在等待,每一个线程会接收到。注意,这里是当上一个线程解锁后,下一个线程才回收到这个广播信号
- (void)testCondition {
    NSCondition *condition = [NSCondition new];
    
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1准备上锁");
        [condition lock];
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        [condition unlock];
        NSLog(@"1解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2准备上锁");
        [condition lock];
        NSLog(@"2成功上锁,进入等待");
        if ([condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:20]]) {// 等一个信号,或者超时, 如果这个线程还使用了lock 需要放在lock 之后
            NSLog(@"2收到了信号");
        }else {
            NSLog(@"2超时");
        }
        
        int time = arc4random() %5;
        NSLog(@"2线程:%d",time);
        sleep(time);
        [condition unlock];
        NSLog(@"2解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"3准备上锁");
        [condition lock];
        NSLog(@"3成功上锁,进入等待");
        [condition wait];// 不设置超时 一直等待知道收到信号
        int time = arc4random() %5;
        NSLog(@"3线程:%d",time);
        sleep(time);
        [condition unlock];
        NSLog(@"3解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"4准备上锁");
        [condition lock];
        NSLog(@"4成功上锁");
        int time = arc4random() %5;
        NSLog(@"4线程:%d",time);
        sleep(time);
        [condition unlock];
        NSLog(@"4解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"5准备上锁");
        [condition lock];
        NSLog(@"5成功上锁,进入等待");
        if ([condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:50]]) {// 等一个信号,或者超时, 如果这个线程还使用了lock 需要放在lock 之后
            NSLog(@"5收到了信号");
        }else {
            NSLog(@"5超时");
        }
        
        int time = arc4random() %5;
        NSLog(@"5线程:%d",time);
        sleep(time);
        [condition unlock];
        NSLog(@"5解锁");
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"发送了一个信号");
//        [condition signal];
        // 或者广播一个信号,
        [condition broadcast];
    });
}

NSConditionLock

  • NSConditionLock 是对 NSCondition 的再一次封装
  • Condition 等于设置的数字时,才可以加锁成功
  • 通过数字来实现
  • 可以实现任务之间的相互依赖
- (void)testNSConditionLock {
    NSConditionLock *oneLock = [[NSConditionLock alloc] initWithCondition:0];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1准备上锁");
        [oneLock lock];
        NSLog(@"1成功上锁");
        
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        [oneLock unlock];
        NSLog(@"1解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2准备上锁");
        [oneLock lockWhenCondition:0];
        NSLog(@"2成功上锁");
        
        int time = arc4random() %5;
        NSLog(@"2线程:%d",time);
        sleep(time);
        [oneLock unlockWithCondition:3];
        NSLog(@"2解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"3准备上锁");
        [oneLock lockWhenCondition:3];
        NSLog(@"3成功上锁");
        
        int time = arc4random() %5;
        NSLog(@"3线程:%d",time);
        sleep(time);
        [oneLock unlockWithCondition:4];
        NSLog(@"3解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"4准备上锁");
        [oneLock lockWhenCondition:4];
        NSLog(@"4成功上锁");
        
        int time = arc4random() %5;
        NSLog(@"4线程:%d",time);
        sleep(time);
        [oneLock unlockWithCondition:5];
        NSLog(@"4解锁");
    });
}

读写锁

- (void)testrwLock {
    static pthread_rwlock_t rwlock_t;
    pthread_rwlock_init(&rwlock_t, nil);
    for (int i = 0; i< 10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%dread ready",i);
            pthread_rwlock_rdlock(&rwlock_t);
            int time = arc4random() %5;
            NSLog(@"%dreading:%d",i,time);
            sleep(time);
            NSLog(@"%dreaded",i);
            pthread_rwlock_unlock(&rwlock_t);
        });
    }
    for (int i = 10; i< 20; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%dwrite ready",i);
            pthread_rwlock_wrlock(&rwlock_t);
            int time = arc4random() %5;
            NSLog(@"%dwriting:%d",i,time);
            sleep(time);
            NSLog(@"%dwriteded",i);
            pthread_rwlock_unlock(&rwlock_t);
        });
    }
    for (int i = 20; i< 30; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%dread ready",i);
            pthread_rwlock_rdlock(&rwlock_t);
            int time = arc4random() %5;
            NSLog(@"%dreading:%d",i,time);
            sleep(time);
            NSLog(@"%dreaded",i);
            pthread_rwlock_unlock(&rwlock_t);
        });
    }
}

synchronized

  • 一个简单的,性能偏低的,内部实现递归的锁
- (void)testsynchronized {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1准备上锁");
        @synchronized (self) {
            NSLog(@"1成功上锁");
            
            int time = arc4random() %5;
            NSLog(@"1线程:%d",time);
            sleep(time);
        }
        NSLog(@"1解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2准备上锁");
        @synchronized (self) {
            NSLog(@"2成功上锁");
            
            int time = arc4random() %5;
            NSLog(@"2线程:%d",time);
            sleep(time);
        }
        NSLog(@"2解锁");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"3准备上锁");
        @synchronized (self) {
            NSLog(@"3成功上锁");
            
            int time = arc4random() %5;
            NSLog(@"3线程:%d",time);
            sleep(time);
        }
        NSLog(@"3解锁");
    });
}

信号量

- (void)testSemphore{
    dispatch_semaphore_t semp = dispatch_semaphore_create(1); //传入值必须 >=0, 若传入为0则阻塞线程并等待timeout,时间到后会执行其后的语句
    dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 5.0f * NSEC_PER_SEC);

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"1准备上锁");
        dispatch_semaphore_wait(semp, overTime);// 如果signal > 0 则-1 继续向下执行,如果signal == 0 则等待
        int time = arc4random() %5;
        NSLog(@"1线程:%d",time);
        sleep(time);
        dispatch_semaphore_signal(semp); // singal +1
        NSLog(@"1解锁");
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2准备上锁");
        dispatch_semaphore_wait(semp, overTime);
        int time = arc4random() %5;
        NSLog(@"2线程:%d",time);
        sleep(time);
        dispatch_semaphore_signal(semp);
        NSLog(@"2解锁");
    });
}

总结:

  • 什么时候使用锁?
    • 当多个线程同时访问同一块资源的时候,需要使用锁
  • 什么情况下使用什么样的锁?
    • 自旋锁:循环等待锁,一直占用着CPU资源,如果临界资源小,处理资源耗时少,则可以使用,如:从内存中直接获取
    • 不公平锁:取代自旋锁的,当你在iOS10 后,想用自旋锁的时候,就用这个
    • 互斥锁:线程调度消耗CPU资源,如果资源大,处理资源耗时长,则可以使用,如:从网络中获取资源,大量计算等
    • 读写锁:当多个线程对同一个资源存在大量读取操作,少量写操作时使用,
    • synchronized: 当你懒的时候使用,虽然性能不怎么好,但是简单

标签:IOS,pthread,NSLog,dispatch,线程,关于,time,mutex
来源: https://blog.csdn.net/wutao63/article/details/122428961

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有