IOS面试题(多线程) --- 锁

2022-04-29  本文已影响0人  ShawnAlex

OC面试题目合集地址

问题1: ios当中都有哪些锁

基本的锁:自旋锁互斥锁读写锁,其他的比如条件锁递归锁信号量都是上层的封装和实现。


问题2: 说一下锁的性能

图摘选自互联网

锁的性能从高到底依次是:

OSSpinLock(自旋锁) >
dispatch_semaphone(信号量) >
pthread_mutex(互斥锁)>
NSLock(互斥锁)>
NSCondition(条件锁) >
pthread_mutex(recursive 互斥递归锁)>
NSRecursiveLock(递归锁)>
NSConditionLock(条件锁)>
synchronized(互斥锁)


问题3: 看下面例子会发生什么

@interface ViewController ()

@property (nonatomic, strong) NSLock *lock;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    
    self.lock = [[NSLock alloc] init];
    
    [self test1];
}

- (void)test1 {


    [self.lock lock];
    NSLog(@"执行 test1");
    [self test2];
    [self.lock unlock];


}

- (void)test2 {

    [self.lock lock];
    NSLog(@"执行 test2");
    [self.lock unlock];

}

答案

会发生死锁

死锁验证

使用NSLock对临界区进行加锁, 进入方法2, 方法2里面对同一把锁又进行了lock方法, 这种情况会由于重入原因发生死锁

问题追问: 那上面例子怎么解决

用递归锁解决, 利用其重入特性: 一个线程可以加N次锁而不会引发死锁

@interface ViewController ()

@property (nonatomic, strong) NSRecursiveLock *reclock;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    self.reclock = [[NSRecursiveLock alloc] init];
    [self testA];
}

- (void)testA {

    [self.reclock lock];
    NSLog(@"执行 testA");
    [self testB];
    [self.reclock unlock];

}

- (void)testB {

    [self.reclock lock];
    NSLog(@"执行 testB");
    [self.reclock unlock];

}

正确解决方法

问题4: 稍微说一说信号量

dispatch_semaphore_t的关键3个方法

dispatch_semaphore_create(intptr_t value);

dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

dispatch_semaphore_signal(dispatch_semaphore_t dsema);

所以看出信号量一般处理 加锁(互斥), 异步返回, 控制线程并发数这些场景。


问题5: 火车票售卖的设计思路

重点考察多线程并发安全这一情况, 下面例子都以总20张票, 卖10张, 模拟一下:

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, assign) int I;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

   // 初始总票数20
    self.i = 20;

    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 模拟或者票抢票, 异步并发
    for (int l = 0; l < 10; l++) {

        dispatch_async(quene, ^{
            [self sellingTickets];
        });
    }
    
}

// 卖票方法
- (void)sellingTickets {
    
    sleep(1);
    self.i--;
    NSLog(@"卖出1张票, 剩余 %d 张", self.i);   
}

@end
错误答案1

可看出数据其实是错乱的

那么我们肯定是要加互斥锁, 来保证线程安全, 如下


    self.i = 20;

    NSLock *lock = [[NSLock alloc] init];
    
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    for (int l = 0; l < 10; l++) {

        dispatch_async(quene, ^{
            [lock lock];
            [self sellingTickets];
            [lock unlock];
        });
    }

方法1

可看出数据正常, 方法没问题是可以的。

这里再提供另外一个解法, 信号量处理。信号量优势看下 问题2 锁的性能 , 其中有dispatch_semaphone(信号量) >NSLock(互斥锁)。 并且因为这道题数据量比较小, 如果复杂同步操作, 还是用信号量好一些, 那么我们用信号量保证下线程安全

    self.i = 20;

    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

    for (int l = 0; l < 10; l++) {

        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            [self sellingTickets];
            dispatch_semaphore_signal(semaphore);
        });
    }
信号量解法
上一篇 下一篇

猜你喜欢

热点阅读