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

OC面试题目合集地址

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

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

  • 自旋锁: 循环等待询问, 不释放当前资源 ( 线程会反复检查变量是否可用), 因为开销很大, 通常用于轻量级数据访问。一旦使用自旋锁, 线程会一直保持该锁, 直到显式释放自旋锁。

    • OSSpinLock (IOS10之后废弃)
    • atomic
  • 互斥锁: 防止2条线程同时对同一公共资源进行读写机制, 保证各个线程互斥在各自的临界区

    • @synchronized
    • NSLock:
    • pthread_mutex
  • 条件锁: 通过条件变量控制, 不满足资源要求锁住进行休眠, 满足开锁正常运行

    • NSCondition
    • NSConditionLock
  • 递归锁: 一个线程可以加N次锁而不会引发死锁。递归锁其实是带有递归性质的互斥锁

    • pthread_mutex(recursive)
    • NSRecursiveLock
  • 信号量: 类似于计算机中记录型信号量是一种更高级的同步机制, 互斥锁其实是信号量semaphore仅在0, 1特例, 但信号量semaphore有更多取值空间, 通常用于实现复杂同步操作, 不仅仅线程间互斥。

    • dispatch_semaphore_t
  • 读写锁: 一个读写锁同时只能有一个写者或者多个读者, 其本质是一种特殊的自旋锁, 提高了并发性。对共享资源的访问分成读者写者。其中读者只对共享资源进行读访问写者则对共享资源进行写操作

  • dispatch_barrier_async



问题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);
  • dispatch_semaphore_create(intptr_t value);: 初始化信号量, 传入参数表示为传入多少信号量

  • dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);: 操作对信号量-1

    • 第一个参数dsema: 表示对哪个参数进行操作。即 -1, 如果参数为0, 则等待直到 > 0
    • 第二个参数timeout: 表示等待时长
  • dispatch_semaphore_signal(dispatch_semaphore_t dsema);: 对信号量+1操作,dsema参数表示对哪个信号量进行+1

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



问题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);
        });
    }
信号量解法

你可能感兴趣的:(IOS面试题(多线程) --- 锁)