浅谈iOS中的锁的介绍及实战使用

前言

  • 这篇文章咱们来聊聊锁,先来看看分析各类锁以前的性能的图表:

同步锁

1.@synchronized (self)

实现锁的优势就是咱们不须要在代码中显式的建立锁对象,即可以实现锁的机制git

@synchronized (self) {
    //TODO:加锁操做
}
复制代码

互斥锁

互斥锁是用来保证共享数据操做的完整性。每一个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问对象github

1.NSLock

NSLock遵循NSLocking协议数组

@protocol NSLocking
- (void)lock;//加锁
- (void)unlock;//解锁
@end
复制代码

NSLock锁较为经常使用,一般是添加在一个线程中,要注意的是添加锁的线程不要是屡次执行的,由于添加锁以后,其余线程要等待锁执行以后才能执行,因此添加锁的的代码最好是不耗时markdown

- (BOOL)tryLock;//尝试加锁
//指定Date以前尝试加锁,若是在指定时间以前都不能加锁
- (BOOL)lockBeforeDate:(NSDate *)limit;
复制代码

使用示例多线程

NSLock *lock = [NSLock new];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"线程1 尝试加速ing...");
    [lock lock];
    sleep(3);//睡眠5秒
    NSLog(@"线程1");
    [lock unlock];
    NSLog(@"线程1解锁成功");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"线程2 尝试加速ing...");
    BOOL x =  [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4]];
    if (x) {
        NSLog(@"线程2");
        [lock unlock];
    }else{
        NSLog(@"失败");
    }
});
复制代码

2.pthread

pthread除了建立互斥锁,还能够建立递归锁、读写锁、once等锁async

- (void)lock{
    __block pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"+++++ 线程1 start");
        pthread_mutex_lock(&mutex);
        sleep(2);
        pthread_mutex_unlock(&mutex);
        NSLog(@"+++++ 线程1 end");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"----- 线程2 start");
        pthread_mutex_lock(&mutex);
        sleep(3);
        pthread_mutex_unlock(&mutex);
        NSLog(@"----- 线程2 end");
    });
}
复制代码

条件锁

1.NSCondition

NSCondition 的对象其实是做为一个锁和线程检查器,锁主要是为了检测条件时保护数据源,执行条件引起的任务。线程检查器主要是根据条件决定是否继续运行线程,即线程是否被阻塞。函数

- (NSArray*)removeLastImage:(NSMutableArray *)images {
    if (images.count > 0) {
        NSCondition *condition = [[NSCondition alloc] init];
        [condition lock];
        [images removeLastObject];
        [condition unlock];
        return images.copy;
    }else{
        return NULL;
    }
}
复制代码

NSCondition能够给每一个线程分别加锁,加锁后不影响其余线程进入临界区oop

- (void)testLock{
    self.conditionArray = [NSMutableArray array];
    self.condition = [[NSCondition alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.condition lock];
        if (self.conditionArray.count == 0) {
            NSLog(@"等待制做数组");
            [self.condition wait];
        }
        NSLog(@"获取对象进行操做:%@",self.conditionArray[0]);
        [self.condition unlock];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.condition lock];
        id obj = @"xxxxxxx";
        [self.conditionArray addObject:obj];
        NSLog(@"建立了一个对象:%@",obj);
        [self.condition signal];
        [self.condition unlock];
    });
}
复制代码
  • 等待2秒
NSCondition *cLock = [NSCondition new];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"start");
    [cLock lock];
    [cLock waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
    NSLog(@"线程1");
    [cLock unlock];
});
复制代码
  • 唤醒一个等待线程
NSCondition *cLock = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [cLock lock];
    NSLog(@"线程1加锁成功");
    [cLock wait];
    NSLog(@"线程1");
    [cLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [cLock lock];
    NSLog(@"线程2加锁成功");
    [cLock wait];
    NSLog(@"线程2");
    [cLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(2);
    NSLog(@"唤醒一个等待的线程");
    [cLock signal];
});
复制代码
  • 唤醒全部等待的线程
.........    
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(2);
    NSLog(@"唤醒全部等待的线程");
    [cLock broadcast];
});
复制代码

2.NSContionLock

只有 condition 参数与初始化时候的 condition 相等,才能正确进行加锁操做。而 unlockWithCondition: 并非当 Condition 符合条件时才解锁,而是解锁以后,修改 Condition 的值。性能

- (void)testLock{
    NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0;i < 6;i++) {
            [conditionLock lock];
            NSLog(@"thread1:%d",i);
            sleep(2);
            [conditionLock unlockWithCondition:i];
        }
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [conditionLock lockWhenCondition:2];
        NSLog(@"thread2");
        [conditionLock unlock];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [conditionLock lockWhenCondition:3];
        NSLog(@"thread3");
        [conditionLock unlock];
    });
}
复制代码

递归锁

1.NSRecursiveLock

NSRecursiveLock 是递归锁,他和 NSLock 的区别在于,NSRecursiveLock 能够在一个线程中重复加锁(反正单线程内任务是按顺序执行的,不会出现资源竞争问题),NSRecursiveLock 会记录上锁和解锁的次数,当两者平衡的时候,才会释放锁,其它线程才能够上锁成功。spa

- (void)lock4 {
    NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^TestMethod)(int);
        TestMethod = ^(int value) {
            [recursiveLock lock];
            if (value > 0) {
                NSLog(@"加锁层数: %d",value);
                TestMethod(--value);
            }
            NSLog(@"程序退出!");
            [recursiveLock unlock];
        };
        TestMethod(3);
    });
}
复制代码

pthread

- (void)lock6 {
    __block pthread_mutex_t recursiveMutex;
    pthread_mutexattr_t recursiveMutexattr;
    pthread_mutexattr_init(&recursiveMutexattr);
    pthread_mutexattr_settype(&recursiveMutexattr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&recursiveMutex, &recursiveMutexattr);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^TestMethod)(int);
        TestMethod = ^(int  value) {
            pthread_mutex_lock(&recursiveMutex);
            if (value > 0) {
                NSLog(@"加锁层数: %d",value);
                sleep(1);
                TestMethod(--value);
            }
            NSLog(@"程序退出!");
            pthread_mutex_unlock(&recursiveMutex);
        };
        TestMethod(3);
    });
}
复制代码

自旋锁

是用于多线程同步的一种锁,线程反复检查锁变量是否可用。因为线程在这一过程当中保持执行,所以是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。 自旋锁避免了进程上下文的调度开销,所以对于线程只会阻塞很短期的场合是有效的。

1.OSSpinLock

__block OSSpinLock oslock = OS_SPINLOCK_INIT;
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"线程1 准备上锁");
    OSSpinLockLock(&oslock);
    sleep(4);
    NSLog(@"线程1");
    OSSpinLockUnlock(&oslock);
    NSLog(@"线程1 解锁成功");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"线程2 准备上锁");
    OSSpinLockLock(&oslock);
    NSLog(@"线程2");
    OSSpinLockUnlock(&oslock);
    NSLog(@"线程2 解锁成功");
});
复制代码

信号量

1.dispatch_semaphore

若是获取不到锁,会将当前线程阻塞、休眠,直到其余线程释放锁时,会唤醒当前线程。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"task A");
    dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"task B");
    dispatch_semaphore_signal(semaphore);
});
复制代码

分布锁

1.NSDistributedLock

NSDistributedLock *lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/lock.lock"];
while (![lock tryLock]){
    sleep(1);
}
//do something
[lock unlock];
复制代码

死锁

死锁是因为多个线程(进程)在执行过程当中,由于争夺资源而形成的互相等待现象,你能够理解为卡主了。产生死锁的必要条件有四个:

  • 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。若是此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
  • 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对本身已得到的其它资源保持不放。
  • 不可剥夺条件:指进程已得到的资源,在未使用完以前,不能被剥夺,只能在使用完时由本身释放。
  • 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
NSLock *rLock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    static void (^TestBlock)(int);
    TestBlock = ^(int value) {
        [rLock lock];
        if (value > 0) {
            NSLog(@"线程%d", value);
            TestBlock(value - 1);
        }
        [rLock unlock];
    };
    TestBlock(4);
});
复制代码

最多见的就是 同步函数 + 主队列 的组合,本质是队列阻塞。
死锁是因为阻塞闭环形成的,那么咱们只用消除其中一个因素,就能打破这个闭环,避免死锁。

参考文档

理解GCD死锁
iOS 中几种经常使用的锁总结
iOS 中常见的几种锁-代码示例
iOS进阶-细数iOS中的锁

关于锁介绍和实战使用介绍就到此完毕,后面有相关再补充,写文章不容易,还请点个**小星星**传送门

相关文章
相关标签/搜索