MST
星途 面试题库

面试题:Objective-C可变数组在多线程环境下的应用与同步机制

在多线程编程中,Objective-C的可变数组可能会出现数据竞争等问题。请详细说明如何确保可变数组在多线程环境下的安全使用,包括不同同步机制(如锁、GCD等)的选择和实现方式,分析每种同步机制在这种场景下的利弊。
45.6万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

1. 使用锁(NSLock、@synchronized 等)

  • NSLock
    • 实现方式
NSLock *lock = [[NSLock alloc] init];
NSMutableArray *mutableArray = [NSMutableArray array];

// 在访问可变数组前加锁
[lock lock];
[mutableArray addObject:@"element"];
[lock unlock];
- **利弊**:
    - **利**:简单直观,易于理解和实现。
    - **弊**:加锁和解锁操作增加了代码复杂度,如果在不合适的地方加锁或解锁,容易出现死锁问题。同时,锁的粒度不易控制,如果锁的范围过大,会影响程序的并发性能。
  • @synchronized
    • 实现方式
NSMutableArray *mutableArray = [NSMutableArray array];
id someObject = [[NSObject alloc] init];
@synchronized (someObject) {
    [mutableArray addObject:@"element"];
}
- **利弊**:
    - **利**:语法简洁,不需要显式管理锁对象。
    - **弊**:同样存在死锁风险,而且它使用的是递归锁,性能相对较低。并且它锁定的是一个对象,对于多个不同对象可能需要使用不同的锁,管理起来相对复杂。

2. 使用 GCD(Grand Central Dispatch)

  • 串行队列
    • 实现方式
NSMutableArray *mutableArray = [NSMutableArray array];
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
    [mutableArray addObject:@"element"];
});
- **利弊**:
    - **利**:代码简洁,利用 GCD 的队列机制自动实现同步,避免了手动加锁解锁的繁琐操作。性能相对较高,特别是在多核环境下,系统可以高效地调度任务。
    - **弊**:如果所有与可变数组相关的操作都放在串行队列中,可能会影响其他需要并发执行的任务,导致整体性能下降。
  • 并发队列 + 信号量
    • 实现方式
NSMutableArray *mutableArray = [NSMutableArray array];
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(concurrentQueue, ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [mutableArray addObject:@"element"];
    dispatch_semaphore_signal(semaphore);
});
- **利弊**:
    - **利**:结合了并发队列的高性能和信号量的同步功能,既能充分利用多核优势提高并发性能,又能保证可变数组操作的线程安全。
    - **弊**:需要额外管理信号量,增加了一定的代码复杂度,如果信号量的等待和信号操作处理不当,也可能出现死锁或资源泄漏问题。

3. 使用线程安全的集合类

  • NSMutableArray 的线程安全替代品(如 NSMutableCopyOnWriteArray)
    • 实现方式
NSMutableCopyOnWriteArray *mutableArray = [NSMutableCopyOnWriteArray array];
[mutableArray addObject:@"element"];
- **利弊**:
    - **利**:使用简单,无需额外的同步代码,开发者可以像使用普通可变数组一样操作。读操作性能高,因为读操作时不会加锁,而是操作原始数组,只有写操作时才会复制数组。
    - **弊**:写操作性能相对较低,因为每次写操作都需要复制整个数组,消耗额外的内存和时间。不适合频繁写操作的场景。