美文网首页
iOS中的锁

iOS中的锁

作者: xxttw | 来源:发表于2023-06-15 11:04 被阅读0次
    image.png
    OSSpinLock
    • 自旋锁, 等待锁的线程一直处于(busy-wait)忙等, 一直占用着CPU资源
    • 目前已经不推荐使用, 会有优先级反转的问题
    • 如果等待锁的线程优先级较高, 它会一直占用着CPU资源, 优先级低的线程可能无法释放锁
    self.lock = OS_SPINLOCK_INIT; //初始化
    - (void)__saveMoney
    {
        OSSpinLockLock(&_lock);
        [super __saveMoney];
        OSSpinLockUnlock(&_lock);
    }
    
    

    假设有如下两个线程
    线程A: 优先级高
    线程B: 优先级低
    优先级低的线程B, 先获取到了锁, 加锁后, 执行代码, 但由于线程A的优先级比较高, 系统会一直分配CPU资源给A, 线程B就可能一直没有足够的时间执行, 可能就会造成线程B无法解锁, 这样就造成了优先级反转的问题

    os_unfair_lock

    从汇编调用过程来看,其实是一个互斥锁,苹果文档里写的是low-level lock

    • iOS10开始, 用来取代OSSpinlock, 是一种不公平的锁
    • 刚解锁的线程可能会再次获得锁, 其他线程继续等待
    • 等待os_unfair_lock锁的线程是处于休眠状态, 并非忙等
    self.lock = OS_UNFAIR_LOCK_INIT;
    - (void)__saveMoney
    {
        os_unfair_lock_lock(&_lock);
        [super __saveMoney];
        os_unfair_lock_unlock(&_lock);
    }
    
    pthread_mutex_t
    • 互斥锁, Unix,Linux跨平台的锁, 等待锁的线程会处于休眠状态
        pthread_mutex_init(mutex, NULL); // 传null是default
    //    pthread_mutexattr_t attr;
    //    pthread_mutexattr_init(&attr);
    //    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    //    pthread_mutexattr_destroy(&attr);
        pthread_mutex_lock(&_mutex);
        NSLog(@"%s",__func__);
        pthread_mutex_unlock(&_mutex);
    
    pthread_mutex_cond
    • 条件 可以设置pthread_mutex_cond 进行相应的条件等待
            pthread_mutexattr_t attr;
            pthread_mutexattr_init(&attr);
            pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
            pthread_mutex_init(&_mutex, &attr); // 传null是default
            pthread_mutexattr_destroy(&attr);
            pthread_cond_init(&_cond, 0);
    - (void)__remove
    {
        // 假设先调用的删除,但是数组里并没有数据
        pthread_mutex_lock(&_mutex);
        NSLog(@"_remove -- begin");
    
        // 这时,线程进行休眠等待状态,并释放锁, 其他线程可获得锁
        if (self.data.count == 0) {
            pthread_cond_wait(&_cond, &_mutex);
        }
        [self.data removeLastObject];
        
        pthread_mutex_unlock(&_mutex);
    }
    
    - (void)__add
    {
        // add方法的线程获取到锁,进行加锁
        pthread_mutex_lock(&_mutex);
        NSLog(@"_add -- begin");
        [self.data addObject:@"123"];
        // 完成任务 发送条件锁信号,唤醒remove线程继续执行任务
        pthread_cond_signal(&_cond);
        
        pthread_mutex_unlock(&_mutex);
    }
    
    pthread_mutex_recursive
    • 递归锁, 多个线程访问,可以对同一个锁对象进行多次加锁,解锁
    NSLock & NSRecursiveLock
    • pthread_mutex_t 普通锁递归锁的OC封装, 更加面相对象, 方便我们使用
    NSCondition
    • 对 pthread_mutex_t mutexcond(ˈkɒnd) 的封装
    NSConditionLock
    • 是对NSCondition的进一步封装, 封装了条件的具体值
    • 设置锁的条件, 可以让子线程根据条件顺序执行任务
    self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
    - (void)otherTest
    {
        [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
        [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
        [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
    }
    
    - (void)__one
    {
        [self.conditionLock lockWhenCondition:1];
        NSLog(@"__one");
        sleep(1);
        [self.conditionLock unlockWithCondition:2];
    }
    
    - (void)__two
    {
        [self.conditionLock lockWhenCondition:2];
        NSLog(@"__two");
        sleep(1);
        [self.conditionLock unlockWithCondition:3];
    }
    
    - (void)__three
    {
        [self.conditionLock lockWhenCondition:3];
        NSLog(@"__three");
        [self.conditionLock unlockWithCondition:4];
    }
    
    2022-12-19 20:23:04.257210+0800 多线程-安全隐患[74891:9502277] __one
    2022-12-19 20:23:05.262859+0800 多线程-安全隐患[74891:9502278] __two
    2022-12-19 20:23:06.267912+0800 多线程-安全隐患[74891:9502279] __three
    
    dispatch_semaphore todo 补充几个代码中的实际运用, 以及丰富一下解释
    • 控制线程的最大并发数量
      dispatch_semaphore_create(value)
      初始化一个信号量, value为信号量的值
      dispatch_semaphore_wait(信号量对象, 等待的时间)
      信号量等待 当信号量的值 > 0时, 会让信号量的值 - 1, 信号量的值 <= 0时, 其他线程会进入休眠等待
      dispatch_semaphore_signal(信号量对象)
      发送一个信号, 信号量的值 + 1

    • 控制线程的最大并发数量,同一时间最多只有5个线程进入, 5个线程执行完毕后, 在新进入5个线程

    self.moneySemaphore = dispatch_semaphore_create(1);
    - (void)__saveMoney
    {
        dispatch_semaphore_wait(_moneySemaphore, DISPATCH_TIME_FOREVER);
        [super __saveMoney];
        dispatch_semaphore_signal(_moneySemaphore);
    }
    
    self.semaphore = dispatch_semaphore_create(5);
    - (void)otherTest
    {
        for (int i = 0; i < 20; i++) {
            [[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start];
        }
    }
    
    - (void)test {
        
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
        sleep(1);
        NSLog(@"test - %@", [NSThread currentThread]);
        dispatch_semaphore_signal(_semaphore);
    }
    
    
    拓展 多个异步请求任务,相互依赖,请求本身属于异步。c依赖b的结果,b依赖a的结果。如何实现?
    • 如果是异步的任务, 网络请求也是异步, 这样在异步执行的时候, 只要代码执行完 就算任务结束, 但是这个时, 可能只是异步的网络请求发送出去了, 数据并未返回, 其实我们真正想要的效果是 异步请求的数据返回后 才算真正的任务结束
    - (void)otherTest {
        NSURLSession *session = [NSURLSession sharedSession];
        
        dispatch_semaphore_t sem = dispatch_semaphore_create(1);
        dispatch_queue_t q = dispatch_get_global_queue(0, 0);
        dispatch_async(q, ^{
            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                NSLog(@"请求A 返回数据");
                dispatch_semaphore_signal(sem);
    
            }];
            [task resume];
        });
        dispatch_async(q, ^{
            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    
            NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                NSLog(@"请求B 返回数据");
                dispatch_semaphore_signal(sem);
    
            }];
            [task resume];
    
    
        });
        dispatch_async(q, ^{
            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    
            NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                NSLog(@"请求C 返回数据");
                dispatch_semaphore_signal(sem);
    
            }];
            [task resume];
            dispatch_semaphore_signal(sem);
        });
    }
    
    @synchroized(对象)
    • 一个对象关联一把锁, 根据传入的对象的地址获取锁, 底层是hashmap[对象]获取锁, 是对 pthread_mutex_t 递归锁的封装

    相关文章

      网友评论

          本文标题:iOS中的锁

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