iOS-进阶学习面试题

iOS多线程:线程锁

2020-04-05  本文已影响0人  码小菜
别墅

目录
一,安全隐患
二,OSSpinLock
三,os_unfair_lock
四,pthread_mutex
五,NSLock
六,NSRecursiveLock
七,NSCondition
八,NSConditionLock
九,@synchronized
十,总结
十一,pthread_rwlock

一,安全隐患

1,实例

@interface ViewController ()
@property (nonatomic, assign) int money;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self saveAndDrawMoney];
}
- (void)saveAndDrawMoney {
    self.money = 100;
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saveMoney];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self drawMoney];
        }
    });
}

- (void)saveMoney {
    [self _saveMoney];
}
- (void)_saveMoney {
    int oldMoney = self.money;
    sleep(0.3);
    oldMoney += 40;
    self.money = oldMoney;
    NSLog(@"存40元,余额%d元---%@", self.money, [NSThread currentThread]);
}

- (void)drawMoney {
    [self _drawMoney];
}
- (void)_drawMoney {
    int oldMoney = self.money;
    sleep(0.3);
    oldMoney -= 20;
    self.money = oldMoney;
    NSLog(@"取20元,余额%d元---%@", self.money, [NSThread currentThread]);
}
@end

// 打印(最后余额应该是200的)
取20元,余额80元---<NSThread: 0x6000030ad1c0>{number = 7, name = (null)}
存40元,余额140元---<NSThread: 0x6000030a0340>{number = 6, name = (null)}
取20元,余额60元---<NSThread: 0x6000030ad1c0>{number = 7, name = (null)}
存40元,余额100元---<NSThread: 0x6000030a0340>{number = 6, name = (null)}
取20元,余额80元---<NSThread: 0x6000030ad1c0>{number = 7, name = (null)}
存40元,余额120元---<NSThread: 0x6000030a0340>{number = 6, name = (null)}
取20元,余额100元---<NSThread: 0x6000030ad1c0>{number = 7, name = (null)}
存40元,余额140元---<NSThread: 0x6000030a0340>{number = 6, name = (null)}
取20元,余额120元---<NSThread: 0x6000030ad1c0>{number = 7, name = (null)}
存40元,余额160元---<NSThread: 0x6000030a0340>{number = 6, name = (null)}

2,图解

安全隐患

3,解决方案

加锁
二,OSSpinLock

1,使用

#import <libkern/OSAtomic.h>

@interface ViewController ()
@property (nonatomic, assign) OSSpinLock lock;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化锁
    self.lock = OS_SPINLOCK_INIT;
    [self saveAndDrawMoney];
}

- (void)saveMoney {
    // 将_lock锁上
    OSSpinLockLock(&_lock);
    [self _saveMoney];
    // 将_lock打开
    OSSpinLockUnlock(&_lock);
}

- (void)drawMoney {
    // 将_lock锁上
    OSSpinLockLock(&_lock);
    [self _drawMoney];
    // 将_lock打开
    OSSpinLockUnlock(&_lock);
}

// 打印
存40元,余额140元---<NSThread: 0x6000033cfac0>{number = 5, name = (null)}
存40元,余额180元---<NSThread: 0x6000033cfac0>{number = 5, name = (null)}
存40元,余额220元---<NSThread: 0x6000033cfac0>{number = 5, name = (null)}
存40元,余额260元---<NSThread: 0x6000033cfac0>{number = 5, name = (null)}
存40元,余额300元---<NSThread: 0x6000033cfac0>{number = 5, name = (null)}
取20元,余额280元---<NSThread: 0x6000033c7900>{number = 4, name = (null)}
取20元,余额260元---<NSThread: 0x6000033c7900>{number = 4, name = (null)}
取20元,余额240元---<NSThread: 0x6000033c7900>{number = 4, name = (null)}
取20元,余额220元---<NSThread: 0x6000033c7900>{number = 4, name = (null)}
取20元,余额200元---<NSThread: 0x6000033c7900>{number = 4, name = (null)}

2,说明

3,不推荐使用

如果处于忙等状态的线程优先级较高,它会一直占用着CPU资源,优先级较低的线程就无法将锁打开,从而导致死锁

三,os_unfair_lock

1,使用

#import <os/lock.h>

@interface ViewController ()
@property (nonatomic, assign) os_unfair_lock lock;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化锁
    self.lock = OS_UNFAIR_LOCK_INIT;
    [self saveAndDrawMoney];
}

- (void)saveMoney {
    // 将_lock锁上
    os_unfair_lock_lock(&_lock);
    [self _saveMoney];
    // 将_lock打开
    os_unfair_lock_unlock(&_lock);
}

- (void)drawMoney {
    // 将_lock锁上
    os_unfair_lock_lock(&_lock);
    [self _drawMoney];
    // 将_lock打开
    os_unfair_lock_unlock(&_lock);
}

2,说明

四,pthread_mutex
1,normal(互斥锁)
#import <pthread.h>

@interface ViewController ()
@property (nonatomic, assign) pthread_mutex_t mutex;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); // normal
    // 初始化锁
    pthread_mutex_init(&_mutex, &attr);
    // 销毁属性
    pthread_mutexattr_destroy(&attr);
    
    [self saveAndDrawMoney];
}

- (void)saveMoney {
    // 将_mutex锁上
    pthread_mutex_lock(&_mutex);
    [self _saveMoney];
    // 将_mutex打开
    pthread_mutex_unlock(&_mutex);
}

- (void)drawMoney {
    // 将_mutex锁上
    pthread_mutex_lock(&_mutex);
    [self _drawMoney];
    // 将_mutex打开
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc {
    // 销毁锁
    pthread_mutex_destroy(&_mutex);
}

1>初始化锁时,属性可以传NULL

2>等待pthread_mutex的线程会处于休眠状态

2,recursive(递归锁)
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // recursive
    // 初始化锁
    pthread_mutex_init(&_mutex, &attr);
    // 销毁属性
    pthread_mutexattr_destroy(&attr);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self recursive];
    });
}

- (void)recursive {
    // 将_mutex锁上
    pthread_mutex_lock(&_mutex);
    
    static int count = 0;
    count++;
    if (count <= 5) {
        NSLog(@"%s---%@", __func__, [NSThread currentThread]);
        // 递归
        [self recursive];
    }
    
    // 将_mutex打开
    pthread_mutex_unlock(&_mutex);
}

// 打印
-[ViewController recursive]---<NSThread: 0x6000004735c0>{number = 5, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000004735c0>{number = 5, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000004735c0>{number = 5, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000004735c0>{number = 5, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000004735c0>{number = 5, name = (null)}

1>如果递归调用的方法里有用到锁,那么就必须用递归锁,否则会出现死锁

2>同一个线程可以对递归锁进行重复上锁

3,pthread_cond(条件锁)
@interface ViewController ()
@property (nonatomic, assign) pthread_cond_t cond;
@property (nonatomic, strong) NSMutableArray *mArray;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化锁
    pthread_mutex_init(&_mutex, NULL);
    // 初始化条件
    pthread_cond_init(&_cond, NULL);
    
    [self condition];
}

- (void)condition {
    self.mArray = [NSMutableArray new];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        sleep(1.0);
        [self add];
    });
    
    dispatch_async(queue, ^{
        [self remove];
    });
}

- (void)add {
    // 将_mutex锁上
    pthread_mutex_lock(&_mutex);
    
    [self.mArray addObject:@"1"];
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
    // 发送信号
    pthread_cond_signal(&_cond);
    
    // 将_mutex打开
    pthread_mutex_unlock(&_mutex);
}

- (void)remove {
    // 将_mutex锁上
    pthread_mutex_lock(&_mutex);
    
    if (self.mArray.count == 0) {
        // 等待信号
        pthread_cond_wait(&_cond, &_mutex);
    }
    [self.mArray removeLastObject];
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
    
    // 将_mutex打开
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc {
    // 销毁锁
    pthread_mutex_destroy(&_mutex);
    // 销毁条件
    pthread_cond_destroy(&_cond);
}

// 打印
-[ViewController add]---<NSThread: 0x6000005e3980>{number = 5, name = (null)}
-[ViewController remove]---<NSThread: 0x6000005944c0>{number = 7, name = (null)}

1>某个线程中的操作在某种情况下需要依赖另外一个线程中的操作,那么就可以用条件来实现

2>pthread_cond_wait函数会让线程进入休眠状态,并且将锁打开

3>当收到信号时,pthread_cond_wait函数会唤醒线程,并且将锁再次锁上

五,NSLock

它是对pthread_mutex(normal)的封装

@interface ViewController ()
@property (nonatomic, strong) NSLock *lock;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化锁
    self.lock = [NSLock new];
    [self saveAndDrawMoney];
}

- (void)saveMoney {
    // 将lock锁上
    [self.lock lock];
    [self _saveMoney];
    // 将lock打开
    [self.lock unlock];
}

- (void)drawMoney {
    // 将lock锁上
    [self.lock lock];
    [self _drawMoney];
    // 将lock打开
    [self.lock unlock];
}
六,NSRecursiveLock

它是对pthread_mutex(recursive)的封装

@interface ViewController ()
@property (nonatomic, strong) NSRecursiveLock *lock;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化锁
    self.lock = [NSRecursiveLock new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self recursive];
    });
}

- (void)recursive {
    // 将lock锁上
    [self.lock lock];
    
    static int count = 0;
    count++;
    if (count <= 5) {
        NSLog(@"%s---%@", __func__, [NSThread currentThread]);
        // 递归
        [self recursive];
    }
    
    // 将lock打开
    [self.lock unlock];
}
七,NSCondition

它是对pthread_mutexpthread_cond的封装

@interface ViewController ()
@property (nonatomic, strong) NSCondition *lock;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化锁
    self.lock = [NSCondition new];
    [self condition];
}

- (void)add {
    // 将lock锁上
    [self.lock lock];

    [self.mArray addObject:@"1"];
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
    // 发送信号
    [self.lock signal];

    // 将lock打开
    [self.lock unlock];
}

- (void)remove {
    // 将lock锁上
    [self.lock lock];

    if (self.mArray.count == 0) {
        // 等待信号
        [self.lock wait];
    }
    [self.mArray removeLastObject];
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);

    // 将lock打开
    [self.lock unlock];
}
八,NSConditionLock

它是对NSCondition的封装,可以利用条件值来控制线程的执行顺序

@interface ViewController ()
@property (nonatomic, strong) NSConditionLock *lock;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 条件值初始化为1
    self.lock = [[NSConditionLock alloc] initWithCondition:1];
    [self condition];
}

- (void)condition {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        [self one];
    });

    dispatch_async(queue, ^{
        [self two];
    });
    
    dispatch_async(queue, ^{
        [self three];
    });
}

- (void)one {
    // 条件值为3往下执行,否则等待
    [self.lock lockWhenCondition:3];

    NSLog(@"%s---%@", __func__, [NSThread currentThread]);

    // 不设置条件值
    [self.lock unlock];
    
    NSLog(@"当前条件值---%zd", self.lock.condition);
}

- (void)two {
    // 条件值为2往下执行,否则等待
    [self.lock lockWhenCondition:2];

    NSLog(@"%s---%@", __func__, [NSThread currentThread]);

    // 条件值设置为3
    [self.lock unlockWithCondition:3];
    
    NSLog(@"当前条件值---%zd", self.lock.condition);
}

- (void)three {
    // 条件值为1往下执行,否则等待
    [self.lock lockWhenCondition:1];

    NSLog(@"%s---%@", __func__, [NSThread currentThread]);

    // 条件值设置为2
    [self.lock unlockWithCondition:2];
    
    NSLog(@"当前条件值---%zd", self.lock.condition);
}

// 打印
-[ViewController three]---<NSThread: 0x600002ae7400>{number = 4, name = (null)}
当前条件值---2
-[ViewController two]---<NSThread: 0x600002ae2680>{number = 6, name = (null)}
当前条件值---3
-[ViewController one]---<NSThread: 0x600002adfa80>{number = 7, name = (null)}
当前条件值---3
九,@synchronized

1,实例代码

- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self recursive];
    });
}

- (void)recursive {
    @synchronized (self) { // 调用objc_sync_enter函数
        static int count = 0;
        count++;
        if (count <= 5) {
            NSLog(@"%s---%@", __func__, [NSThread currentThread]);
            [self recursive];
        } 
    } // 调用objc_sync_exit函数
}

// 打印
-[ViewController recursive]---<NSThread: 0x6000037a5280>{number = 7, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000037a5280>{number = 7, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000037a5280>{number = 7, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000037a5280>{number = 7, name = (null)}
-[ViewController recursive]---<NSThread: 0x6000037a5280>{number = 7, name = (null)}

2,底层代码(源码下载地址

int objc_sync_enter(id obj)
{
    int result = OBJC_SYNC_SUCCESS;
    
    if (obj) {
        // 用obj生成一个锁
        SyncData* data = id2data(obj, ACQUIRE);
        assert(data);
        // 将锁锁上
        data->mutex.lock();
    } else {
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        objc_sync_nil();
    }
    
    return result;
}

typedef struct alignas(CacheLineSize) SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object;
    int32_t threadCount;  // number of THREADS using this block
    // 递归锁
    recursive_mutex_t mutex;
} SyncData;
int objc_sync_exit(id obj)
{
    int result = OBJC_SYNC_SUCCESS;
    
    if (obj) {
        // 用obj获取之前生成的锁
        SyncData* data = id2data(obj, RELEASE);
        if (!data) {
            result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
        } else {
            // 将锁打开
            bool okay = data->mutex.tryUnlock();
            if (!okay) {
                result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
            }
        }
    } else {
        // @synchronized(nil) does nothing
    }
    
    return result;
}
十,总结

1,关系图

关系图

2,性能图

性能图

⚠️注意⚠️:因为休眠和唤醒比忙等更加消耗CPU资源,所以互斥锁比自旋锁性能低

十一,pthread_rwlock

1,说明

1>同一时间,允许有多个线程进行读的操作

2>同一时间,只能有一个线程进行写的操作

3>同一时间,不允许既有写的操作,又有读的操作

2,使用

@interface ViewController ()
@property (nonatomic, assign) pthread_rwlock_t lock;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化锁
    pthread_rwlock_init(&_lock, NULL);
    [self readAndWrite];
}

- (void)readAndWrite {
    for (int i = 0; i < 5; i++) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self read];
            [self write];
        });
    }
}

- (void)read {
    // 将_lock锁上(读)
    pthread_rwlock_rdlock(&_lock);
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
    sleep(1.0);
    // 将_lock打开
    pthread_rwlock_unlock(&_lock);
}

- (void)write {
    // 将_lock锁上(写)
    pthread_rwlock_wrlock(&_lock);
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
    sleep(1.0);
    // 将_lock打开
    pthread_rwlock_unlock(&_lock);
}

- (void)dealloc {
    // 销毁锁
    pthread_rwlock_destroy(&_lock);
}

// 打印(read同时执行,write顺序执行,read和write没有同时执行)
22:42:04.029314+0800 Demo[68231:11513342] -[ViewController read]---<NSThread: 0x600003a65140>{number = 5, name = (null)}
22:42:04.029350+0800 Demo[68231:11513347] -[ViewController read]---<NSThread: 0x600003a6af80>{number = 3, name = (null)}
22:42:04.029384+0800 Demo[68231:11513340] -[ViewController read]---<NSThread: 0x600003a44d00>{number = 6, name = (null)}
22:42:04.029378+0800 Demo[68231:11513341] -[ViewController read]---<NSThread: 0x600003a6af00>{number = 4, name = (null)}
22:42:04.029697+0800 Demo[68231:11513354] -[ViewController read]---<NSThread: 0x600003a49600>{number = 7, name = (null)}
22:42:05.034127+0800 Demo[68231:11513342] -[ViewController write]---<NSThread: 0x600003a65140>{number = 5, name = (null)}
22:42:06.039197+0800 Demo[68231:11513340] -[ViewController write]---<NSThread: 0x600003a44d00>{number = 6, name = (null)}
22:42:07.043600+0800 Demo[68231:11513347] -[ViewController write]---<NSThread: 0x600003a6af80>{number = 3, name = (null)}
22:42:08.044081+0800 Demo[68231:11513341] -[ViewController write]---<NSThread: 0x600003a6af00>{number = 4, name = (null)}
22:42:09.049382+0800 Demo[68231:11513354] -[ViewController write]---<NSThread: 0x600003a49600>{number = 7, name = (null)}
上一篇 下一篇

猜你喜欢

热点阅读