MST

星途 面试题库

面试题:Objective-C的GCD并发队列在多核设备上的性能优化策略

在Objective-C开发中,基于GCD并发队列,考虑多核设备的特性,阐述如何对并发任务进行性能优化。例如,怎样合理分配任务到不同的队列以充分利用多核资源,如何避免线程竞争带来的性能损耗,以及在处理大量并发任务时如何优化内存使用等方面进行详细说明。
47.5万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

合理分配任务到不同队列以充分利用多核资源

  1. 区分任务类型
    • CPU密集型任务:这类任务主要消耗CPU资源,例如复杂的数学计算、图像渲染等。应将其分配到全局并发队列中。因为全局并发队列是系统提供的与系统内核数量相关的并发队列,能够充分利用多核CPU资源。示例代码如下:
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
    // CPU密集型任务代码,如复杂数学计算
    for (int i = 0; i < 1000000; i++) {
        double result = sqrt(i * i + 1);
    }
});
- **I/O密集型任务**:像文件读写、网络请求等I/O操作属于此类任务。可以创建自己的并发队列来处理。因为I/O操作通常会有等待时间,在等待I/O完成时,其他线程可以利用CPU资源执行其他任务。示例代码:
dispatch_queue_t ioQueue = dispatch_queue_create("com.example.ioQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(ioQueue, ^{
    // I/O密集型任务,如文件读取
    NSData *data = [NSData dataWithContentsOfFile:@"path/to/file"];
});
  1. 任务粒度控制
    • 对于CPU密集型任务,如果任务粒度太大,一个任务长时间占用CPU,会导致其他任务等待。应将大任务拆分成多个小任务,然后并发执行。例如,对一个大数据集进行排序,可以将数据集分成若干小块,分别在不同的队列中排序,最后合并结果。
    • 对于I/O密集型任务,虽然I/O操作本身无法并行化,但可以通过批量处理来减少I/O操作的次数。比如,批量读取多个文件时,可以在一个任务中依次读取多个文件,而不是为每个文件创建一个单独的任务。

避免线程竞争带来的性能损耗

  1. 使用线程安全的数据结构
    • 当多个线程需要访问和修改共享数据时,使用线程安全的数据结构可以避免数据竞争。例如,NSMutableArray不是线程安全的,如果多个线程同时修改它,可能会导致数据损坏。可以使用NSMutableArray的线程安全替代品NSMutableOrderedSet,或者使用dispatch_queue_t来串行化对NSMutableArray的访问。示例代码:
NSMutableArray *sharedArray = [NSMutableArray array];
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
    [sharedArray addObject:@"Object1"];
});
dispatch_async(serialQueue, ^{
    [sharedArray removeObjectAtIndex:0];
});
  1. 锁机制
    • 互斥锁(Mutex):使用dispatch_semaphore_t来实现互斥锁。当一个线程获取到信号量(即锁)时,其他线程必须等待。例如,在多个线程访问共享资源时:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(globalQueue, ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // 访问共享资源代码
    dispatch_semaphore_signal(semaphore);
});
- **读写锁**:如果共享资源读操作远多于写操作,可以使用读写锁来提高性能。在Objective - C中可以通过`pthread_rwlock_t`实现。读操作时允许多个线程同时进行,写操作时则独占资源。示例代码:
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
// 读操作
dispatch_async(globalQueue, ^{
    pthread_rwlock_rdlock(&rwlock);
    // 读取共享资源代码
    pthread_rwlock_unlock(&rwlock);
});
// 写操作
dispatch_async(globalQueue, ^{
    pthread_rwlock_wrlock(&rwlock);
    // 写入共享资源代码
    pthread_rwlock_unlock(&rwlock);
});

处理大量并发任务时优化内存使用

  1. 自动释放池(Autorelease Pool)
    • 在处理大量并发任务时,合理使用自动释放池可以减少内存峰值。每个自动释放池会在其生命周期结束时释放池内所有的自动释放对象。例如,在一个循环中创建大量临时对象的任务:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    @autoreleasepool {
        for (int i = 0; i < 10000; i++) {
            NSString *tempString = [NSString stringWithFormat:@"%d", i];
            // 其他操作
        }
    }
});
  1. 对象复用
    • 对于频繁创建和销毁的对象,可以考虑对象复用。例如,在处理网络请求时,如果需要频繁创建NSURLSessionDataTask对象,可以使用对象池来复用这些对象,而不是每次都创建新的对象。可以自己实现一个简单的对象池,维护一个可用对象的队列,需要时从队列中取出对象,使用完毕后再放回队列。
  2. 异步加载与懒加载
    • 在处理大量数据的展示或处理时,采用异步加载和懒加载策略。比如在UITableView中显示大量图片,不要一次性加载所有图片到内存,而是当图片即将显示在屏幕上时再异步加载,加载完成后更新UI。这样可以有效控制内存使用,避免一次性加载大量数据导致内存过高。