面试题答案
一键面试排查资源竞争导致数据不一致问题的方面
- 数据访问点:
- 查找所有对共享数据进行读写操作的代码位置。在多线程环境下,任何对共享数据的并发访问都可能导致问题。例如,在Objective - C中,检查
@property
属性被不同线程访问的地方,特别是那些没有设置合适访问控制的属性。 - 使用工具如 Instruments 的 Thread Sanitizer,它可以检测到数据竞争并指出具体发生竞争的代码行。
- 查找所有对共享数据进行读写操作的代码位置。在多线程环境下,任何对共享数据的并发访问都可能导致问题。例如,在Objective - C中,检查
- 同步机制:
- 检查现有的同步工具使用是否正确。例如,查看是否正确使用了锁(如
NSLock
、NSRecursiveLock
)。确保在访问共享数据前加锁,访问完成后解锁,避免死锁情况(如互相等待对方释放锁)。 - 确认是否有使用信号量(
dispatch_semaphore_t
),检查信号量的初始化值、等待和信号操作是否符合预期逻辑,防止信号量值错误导致线程无法正确同步。
- 检查现有的同步工具使用是否正确。例如,查看是否正确使用了锁(如
- 线程执行顺序:
- 分析线程执行的先后顺序和并发情况。可以通过在关键代码处添加日志输出线程ID和执行步骤,观察多线程执行流程,看是否由于线程执行顺序不当导致数据不一致。
- 利用
NSOperation
的依赖关系来控制执行顺序,确保某些操作在特定操作完成后执行,以避免因错误顺序访问共享数据。
利用NSOperationQueue特性实现高效异步数据处理并避免死锁
- NSOperationQueue特性利用思路:
- 任务优先级:
NSOperation
有queuePriority
属性,可以设置不同任务的优先级。对于重要且对时间敏感的任务,如网络请求的处理,可以设置较高优先级;而一些后台的非关键任务,如数据缓存的更新,可以设置较低优先级。这样可以确保重要任务优先执行,提高整体应用的响应性。 - 任务依赖:通过设置
addDependency:
方法,可以定义任务之间的依赖关系。例如,任务B需要任务A的数据,那么可以让任务B依赖于任务A,保证任务A先执行完成,任务B再开始,从而避免因数据未准备好而导致的错误。 - 最大并发数控制:
NSOperationQueue
有maxConcurrentOperationCount
属性,可以控制同时执行的操作数量。对于一些资源密集型任务,如图片处理,通过设置合适的最大并发数(如根据设备CPU核心数设置),可以避免过多任务同时执行导致资源耗尽,提高系统整体性能。
- 任务优先级:
- 实现方式:
- 创建NSOperation子类:
@interface MyOperation : NSOperation
@property (nonatomic, strong) id data;
- (instancetype)initWithData:(id)data;
@end
@implementation MyOperation
- (instancetype)initWithData:(id)data {
self = [super init];
if (self) {
_data = data;
}
return self;
}
- (void)main {
// 这里执行具体的任务操作,如数据处理
// 例如对_data进行一些计算或者转换
}
@end
- **使用NSOperationQueue**:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 3; // 设置最大并发数
MyOperation *operationA = [[MyOperation alloc] initWithData:@"data for A"];
MyOperation *operationB = [[MyOperation alloc] initWithData:@"data for B"];
MyOperation *operationC = [[MyOperation alloc] initWithData:@"data for C"];
[operationB addDependency:operationA];
[operationC addDependency:operationA];
[queue addOperation:operationA];
[queue addOperation:operationB];
[queue addOperation:operationC];
- **避免死锁**:
在设置任务依赖关系时,要确保不会形成循环依赖。例如,不能出现A依赖B,B依赖C,C又依赖A的情况。在添加依赖前,仔细检查依赖关系的逻辑。同时,在操作共享资源时,正确使用同步机制,如前面提到的锁等工具,按照一定的顺序获取锁,避免死锁。例如,所有线程都按照相同顺序获取锁,先获取锁1,再获取锁2,而不是部分线程先获取锁2再获取锁1。