iOS:多线程安全与Lock

大家好,我是OB!今天来聊聊锁!

Lock 锁:防⽌止多个线程可能会访问同⼀块资源引发的问题,加锁可以保证在多线程环境下的数据访问安全,但同时牺牲了访问效率。
先看看各所锁的性能测试:

性能测试,简单的加解锁,测试结果仅供参考,每次测试结果均不同,但大体一样。测试代码在文章末尾。

Test_Lock[27167:14791830] 0.000257 ->OSSpinLock
Test_Lock[27167:14791830] 0.000399 ->testDispatch_semaphore
Test_Lock[27167:14791830] 0.000411 ->os_unfair_lock_lock
Test_Lock[27167:14791830] 0.000448 ->test_pthread_mutex_lock
Test_Lock[27167:14791830] 0.000504 ->test_pthread_rwlock
Test_Lock[27167:14791830] 0.000789 ->testNSLock
Test_Lock[27167:14791830] 0.000848 ->testRecursiveLock
Test_Lock[27167:14791830] 0.001357 ->testConditionlock
Test_Lock[27167:14791830] 0.001043 ->testSynchronized

可以看出:OSSpinLock效率最高(自旋锁,由于不安全不参与比较,你开挂我就踢你[狗头保命]),
然后前三是:Dispatch_semaphoreos_unfair_lock_lockpthread_mutex_lock
后面三个分别是:NSRecursiveLockNSConditionlock@synchronized

1.自旋锁(OSSpinLock)

是用于多线程同步的种锁,线程反复检查锁变量是否可用,处于忙等状态(一直进行do while循环)

忙等:等待锁的线程会处于忙等(busy-wait)状态,一直占⽤用着CPU资源;
High-level lock,⾼级锁,等不不到锁时忙等,不会休眠

引入#import

  static OSSpinLock spinLock = OS_SPINLOCK_INIT;
  OSSpinLockTry( volatile OSSpinLock *__lock );
  OSSpinLockLock( volatile OSSpinLock *__lock ); 
  OSSpinLockUnlock( volatile OSSpinLock *__lock );

注意: spinLock变量使用static或者__block修饰,不然结果无效

目前已经不再安全,可能会出现优先级反转问题 ;

⾃旋锁优先级反转:有两个线程,thread1(⾼优先级)、thread2(低优先级),thread2先进⾏加锁操作,CPU切换调度,thread1进⼊,发现已经被锁,进⼊自旋状态。由于thread1优先级⽐ thread2⾼出很多,CPU接下来可能⼀直调度thread1,处于自旋状态,相当于⼀直执行不到 thread2的解锁操作,造成优先级反转现象

所以OSSpinLock在 iOS 10 后被 os_unfair_lock_lock 代替,因为前者会出现优先级反转问题(本质已经变成了了互斥锁)
使用和代替它的os_unfair_lock_lock使用一样。

os_unfair_lock_lock 互斥锁

 	os_unfair_lock_trylock(unfairLock);
    os_unfair_lock_lock(unfairLock);
    os_unfair_lock_unlock(unfairLock);

使用如下:

void test_os_unfair_lock_lock() {
    __block os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        os_unfair_lock_lock(&unfairLock);
        NSLog(@"%@ -- start-",[NSThread currentThread]);
        sleep(3);
        NSLog(@"%@ -- end",[NSThread currentThread]);
        os_unfair_lock_unlock(&unfairLock);
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        os_unfair_lock_lock(&unfairLock);
        NSLog(@"%@ -- start-",[NSThread currentThread]);
        sleep(1);
        NSLog(@"%@ -- end",[NSThread currentThread]);
        os_unfair_lock_unlock(&unfairLock);
    });
}

打印:

Test_Lock[13172:12405001] <NSThread: 0x10056e640>{number = 2, name = (null)} -- start-
Test_Lock[13172:12405001] <NSThread: 0x10056e640>{number = 2, name = (null)} -- end
Test_Lock[13172:12405002] <NSThread: 0x100703850>{number = 3, name = (null)} -- start-
Test_Lock[13172:12405002] <NSThread: 0x100703850>{number = 3, name = (null)} -- end

2、信号量:dispatch_semaphore

常用函数API

dispatch_semaphore_t dispatch_semaphore_create(long value)
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

其中出现wait,value将减一,出现signal,value加一,减到0或者负数将会阻塞,创建时传入0,dispatch_semaphore_create(0),如果先遇到wait,那么线程直接阻塞。

创建信号量的时候可以设置最大并发数dispatch_semaphore_create(long value)中的 value就是线程最大并发数。和NSOperationQueue中的maxConcurrentOperationCount作用相似

void testDispatch_semaphore () {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_time_t timeout = DISPATCH_TIME_FOREVER;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(semaphore, timeout);
        NSLog(@"%@ -- start-",[NSThread currentThread]);
        sleep(1);
        NSLog(@"%@ -- end",[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(semaphore, timeout);
        NSLog(@"%@ -- start-",[NSThread currentThread]);
        sleep(1);
        NSLog(@"%@ -- end",[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
}

打印如下

Test_Lock[14273:12433586] <NSThread: 0x10055ce60>{number = 2, name = (null)} -- start-
Test_Lock[14273:12433586] <NSThread: 0x10055ce60>{number = 2, name = (null)} -- end
Test_Lock[14273:12433588] <NSThread: 0x102001560>{number = 3, name = (null)} -- start-
Test_Lock[14273:12433588] <NSThread: 0x102001560>{number = 3, name = (null)} -- end

3.互斥锁:NSLock/@synchronized

互斥锁,当一个线程拥有锁时,其他线程等待,只有当锁释放时,其他线程才能拥有锁,往下执行。

- (void)lock;
- (void)unlock;

使用也简单

void test(NSLock *lock);
void testDispatch_semaphore () {
    NSLock *lock = [[NSLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        test(lock);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        test(lock);
    });
}
static int _tickets = 3;
void test(NSLock *lock) {
    while (1) {
    //@synchronized (token) {
        [lock lock];
        sleep(1);
        if (_tickets > 0) {
            _tickets--;
            NSLog(@"剩余票数= %d, Thread:%@",_tickets,[NSThread currentThread]);
        } else {
            NSLog(@"票卖完了 Thread:%@",[NSThread currentThread]);
            [lock unlock]; //要释放锁
            break;
        }
        [lock unlock];
   //}
    }
}

可将while循环中锁换成@synchronized
打印如下:

Test_Lock[14927:12451072] 剩余票数= 2, Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451072] 剩余票数= 1, Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451072] 剩余票数= 0, Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451072] 票卖完了 Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451071] 票卖完了 Thread:<NSThread: 0x10075ff00>{number = 3, name = (null)}

4.条件锁:NSConditionLock

只有 condition 参数与初始化时候的 condition 相等,conditionlock 才能正确进行加锁操作。
unlockWithCondition: 是解锁之后,修改 condition 的值,并不是当 condition 符合条件时才解锁

void testConditionlock() {
    NSConditionLock * conditionlock = [[NSConditionLock alloc] initWithCondition:0];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [conditionlock lock];
        NSLog(@"1 lock success");
        sleep(1);
        [conditionlock unlockWithCondition:9];
        NSLog(@"1 unlock success");
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [conditionlock lockWhenCondition:9];
        sleep(1);
        NSLog(@"2 lock success");
        [conditionlock unlock];
        NSLog(@"2 unlock success");
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(2);
        if ([conditionlock tryLockWhenCondition:0]) {
            NSLog(@"3 lock success");
            sleep(2);
            [conditionlock unlockWithCondition:2];
            NSLog(@"3 unlock success");
        } else {
            NSLog(@"3 lock fail");
        }
    });
}

打印如下:

Test_Lock[15144:12458143] 1 lock success
Test_Lock[15144:12458143] 1 unlock success
Test_Lock[15144:12458148] 3 lock fail
Test_Lock[15144:12458147] 2 lock success
Test_Lock[15144:12458147] 2 unlock success

5.递归锁:NSRecursiveLock

static NSRecursiveLock *recursiceLock = nil;
void testRecursiveLock(int count) {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        recursiceLock = [[NSRecursiveLock alloc] init];
    });
    if (count <= 0) {
        return;
    }
    NSLog(@"当前数量:%d",count);
    testRecursiveLock(--count);
}

在同⼀一线程上该锁是可重⼊入的,对于不不同线程则相当于 普通的互斥锁

6.读写锁:pthread_rwlock

读写锁,在对文件进行操作的时候,写操作是互斥的,只能一个线程写,写完下个线程再写,写操作的时候,所有读的线程全部阻塞,只有等写操作完成了,才能读。

读操作时,写线程也是要阻塞,等读完释放锁才能写,但是读是可以多个线程一起读的

// 初始化
pthread_rwlock_t lock;
pthread_rwlock_init(&lock, NULL);
// 读模式
pthread_rwlock_wrlock(&lock);
// 写模式
pthread_rwlock_rdlock(&lock);
// 读模式或者写模式的解锁
pthread_rwlock_unlock(&lock);

使用如下


#import 
pthread_rwlock_t lock;
void readBook () {
    pthread_rwlock_rdlock(&lock);
    NSLog(@"start read ---- %@",[NSThread currentThread]);
    sleep(3);
    NSLog(@"end read ---- %@",[NSThread currentThread]);
    pthread_rwlock_unlock(&lock);
}
void writeBook () {
    pthread_rwlock_wrlock(&lock);
    NSLog(@"start write ---- %@",[NSThread currentThread]);
    sleep(4);
    NSLog(@"end write ---- %@",[NSThread currentThread]);
    pthread_rwlock_unlock(&lock);
}



void test_pthread_rwlock () {
    pthread_rwlock_init(&lock, NULL);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        writeBook();
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        writeBook();
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        readBook();
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        readBook();
    });
}

打印如下:

Test_Lock[24941:14660535] start write ---- <NSThread: 0x100504c70>{number = 2, name = (null)}
Test_Lock[24941:14660535] end write ---- <NSThread: 0x100504c70>{number = 2, name = (null)}
Test_Lock[24941:14660537] start write ---- <NSThread: 0x10204c130>{number = 3, name = (null)}
Test_Lock[24941:14660537] end write ---- <NSThread: 0x10204c130>{number = 3, name = (null)}
Test_Lock[24941:14660538] start read ---- <NSThread: 0x10204c220>{number = 4, name = (null)}
Test_Lock[24941:14660539] start read ---- <NSThread: 0x100609560>{number = 5, name = (null)}
Test_Lock[24941:14660538] end read ---- <NSThread: 0x10204c220>{number = 4, name = (null)}
Test_Lock[24941:14660539] end read ---- <NSThread: 0x100609560>{number = 5, name = (null)}

7、还有一个互斥锁:pthread_mutex

他的API

int pthread_mutex_init(pthread_mutex_t * __restrict,
		const pthread_mutexattr_t * _Nullable __restrict);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutex_destroy(pthread_mutex_t *);

使用如下

变量 pthread_mutex_t 必须是static__block 或者全局变量中的一种,不然锁会无效,这里涉及到block的变量捕获原则,必须捕获指针或者是不捕获。局部变量是值捕获,不可以的。

#import 
void test_pthread_mutex_lock() {
    static pthread_mutex_t mutex_lock;
    pthread_mutex_init(&mutex_lock, NULL);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        pthread_mutex_lock(&mutex_lock);
        NSLog(@"start ---- %@",[NSThread currentThread]);
        sleep(4);
        NSLog(@"end ---- %@",[NSThread currentThread]);
        pthread_mutex_unlock(&mutex_lock);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        pthread_mutex_lock(&mutex_lock);
        NSLog(@"start ---- %@",[NSThread currentThread]);
        sleep(2);
        NSLog(@"end ---- %@",[NSThread currentThread]);
        pthread_mutex_unlock(&mutex_lock);
    });
    
}

打印

Test_Lock[26628:14770428] start ---- <NSThread: 0x102007890>{number = 2, name = (null)}
Test_Lock[26628:14770428] end ---- <NSThread: 0x102007890>{number = 2, name = (null)}
Test_Lock[26628:14770429] start ---- <NSThread: 0x10066b190>{number = 3, name = (null)}
Test_Lock[26628:14770429] end ---- <NSThread: 0x10066b190>{number = 3, name = (null)}

测试代码:

#import 
#import 
static OSSpinLock spinLock;
void testOSSpinLock() {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        spinLock = OS_SPINLOCK_INIT;
    });
    OSSpinLockLock(&spinLock);
    OSSpinLockUnlock(&spinLock);
}

static os_unfair_lock unfairLock;
void test_os_unfair_lock_lock() {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        unfairLock = OS_UNFAIR_LOCK_INIT;
    });
    os_unfair_lock_lock(&unfairLock);
    os_unfair_lock_unlock(&unfairLock);
}

static dispatch_semaphore_t semaphoreLock;
void testDispatch_semaphore () {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        semaphoreLock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_signal(semaphoreLock);
}

static NSLock *lock = nil;
void testNSLock () {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        lock = [[NSLock alloc] init];
    });
    [lock lock];
    [lock unlock];
}

static NSConditionLock * conditionlock = nil;
void testConditionlock() {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        conditionlock = [[NSConditionLock alloc] initWithCondition:0];
    });
    [conditionlock lock];
    [conditionlock unlock];
}

static NSRecursiveLock *recursiceLock = nil;
void testRecursiveLock() {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        recursiceLock = [[NSRecursiveLock alloc] init];
    });
    [recursiceLock lock];
    [recursiceLock unlock];
}

static NSString *token =nil;
void testSynchronized () {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        token = @"key_token";
    });
    @synchronized (token) {
        
    }
}
#import 

static pthread_rwlock_t wrlock;
void test_pthread_rwlock () {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        pthread_rwlock_init(&wrlock, NULL);
    });
    pthread_rwlock_wrlock(&wrlock);
    pthread_rwlock_unlock(&wrlock);
}

static pthread_mutex_t mutex_lock;
void test_pthread_mutex_lock() {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        pthread_mutex_init(&mutex_lock, NULL);
    });
    pthread_mutex_lock(&mutex_lock);
    pthread_mutex_unlock(&mutex_lock);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
//        NSDate *date = [[NSDate alloc]init];
        NSTimeInterval ti = [[NSDate new] timeIntervalSince1970];
        NSTimeInterval cur_ti = 0;
        int count = 10000;
        for (int i = 0; i <count; i++) {
            testOSSpinLock();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->OSSpinLock",cur_ti);
        
        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            testDispatch_semaphore();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->testDispatch_semaphore",cur_ti);
        
        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            test_os_unfair_lock_lock();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->os_unfair_lock_lock",cur_ti);

        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            test_pthread_mutex_lock();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->test_pthread_mutex_lock",cur_ti);
        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            test_pthread_rwlock();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->test_pthread_rwlock",cur_ti);
        
        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            testNSLock();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->testNSLock",cur_ti);
        
        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            testRecursiveLock();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->testRecursiveLock",cur_ti);
        
        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            testConditionlock();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->testConditionlock",cur_ti);
        
        ti = [[NSDate new] timeIntervalSince1970];
        for (int i = 0; i <count; i++) {
            testSynchronized();
        }
        cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
        NSLog(@"%f ->testSynchronized",cur_ti);
    }
    return 0;
}

你可能感兴趣的:(iOS进阶)