| 分类 ios  | 标签 iOS  GCD  NSOperation  多线程  并发编程 

引言

在现代移动应用开发中,多线程编程是提升应用性能和用户体验的关键技术。iOS提供了多种多线程解决方案,其中Grand Central Dispatch(GCD)和NSOperation是最为重要和常用的两种技术。本文将深入解析这两种技术的原理、特点、使用场景,并提供详细的实战指南,帮助开发者掌握iOS多线程编程的精髓。

iOS多线程基础概念

进程与线程

进程(Process)是操作系统分配资源的基本单位,每个iOS应用都运行在独立的进程中。进程拥有独立的内存空间,进程间通信需要特殊的机制。

线程(Thread)是CPU调度的基本单位,是进程内的执行单元。同一进程内的线程共享内存空间,但拥有独立的栈空间和程序计数器。

并发与并行

并发(Concurrency):多个任务在同一时间段内交替执行,给人同时执行的错觉。在单核CPU上,所有的多线程都是并发执行。

并行(Parallelism):多个任务在同一时刻真正同时执行。只有在多核CPU上才能实现真正的并行。

iOS线程模型

iOS采用抢占式多任务模型,系统负责线程的调度。主要特点包括:

  1. 主线程(Main Thread):负责UI更新和用户交互,也称为UI线程
  2. 后台线程(Background Thread):执行耗时操作,避免阻塞主线程
  3. 线程安全:多个线程访问共享资源时需要同步机制

Grand Central Dispatch (GCD) 深度解析

GCD架构原理

GCD是Apple开发的一套多线程解决方案,基于C语言实现,提供了简洁而强大的API。GCD的核心概念包括:

1. 队列(Queue) 队列是GCD的核心概念,用于管理任务的执行顺序:

// 串行队列:任务按顺序逐个执行
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);

// 并发队列:多个任务可以同时执行
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);

// 系统提供的全局并发队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 主队列:在主线程上执行任务
dispatch_queue_t mainQueue = dispatch_get_main_queue();

2. 任务(Task) 任务是要执行的代码块,可以是同步或异步执行:

// 异步执行:不等待任务完成,立即返回
dispatch_async(globalQueue, ^{
    // 后台任务
    NSLog(@"Background task");
    
    // 回到主线程更新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        // 更新UI
        self.label.text = @"Task completed";
    });
});

// 同步执行:等待任务完成后返回
dispatch_sync(serialQueue, ^{
    NSLog(@"Synchronous task");
});

GCD队列类型详解

1. 串行队列(Serial Queue)

串行队列中的任务按照FIFO(先进先出)顺序执行,同一时间只能执行一个任务:

@interface SerialQueueExample : NSObject
@property (nonatomic, strong) dispatch_queue_t serialQueue;
@end

@implementation SerialQueueExample

- (instancetype)init {
    self = [super init];
    if (self) {
        _serialQueue = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)demonstrateSerialExecution {
    for (int i = 0; i < 5; i++) {
        dispatch_async(self.serialQueue, ^{
            NSLog(@"Task %d - Thread: %@", i, [NSThread currentThread]);
            [NSThread sleepForTimeInterval:1.0];
        });
    }
    // 输出:任务按顺序执行,都在同一个后台线程上
}

@end

2. 并发队列(Concurrent Queue)

并发队列可以同时执行多个任务,但任务的完成顺序不确定:

- (void)demonstrateConcurrentExecution {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 5; i++) {
        dispatch_async(concurrentQueue, ^{
            NSLog(@"Task %d start - Thread: %@", i, [NSThread currentThread]);
            [NSThread sleepForTimeInterval:arc4random_uniform(3)];
            NSLog(@"Task %d end - Thread: %@", i, [NSThread currentThread]);
        });
    }
    // 输出:任务可能在不同线程上并发执行,完成顺序不确定
}

3. 全局队列(Global Queue)

系统提供的并发队列,有不同的优先级:

// iOS 8.0之前的优先级
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t defaultPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t backgroundPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

// iOS 8.0之后的QoS(Quality of Service)
dispatch_queue_t userInteractiveQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
dispatch_queue_t userInitiatedQueue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0);
dispatch_queue_t utilityQueue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);

GCD高级特性

1. 栅栏函数(Barrier)

栅栏函数在并发队列中创建同步点,确保在栅栏任务执行时,队列中没有其他任务执行:

@interface BarrierExample : NSObject
@property (nonatomic, strong) dispatch_queue_t concurrentQueue;
@property (nonatomic, strong) NSMutableArray *dataArray;
@end

@implementation BarrierExample

- (instancetype)init {
    self = [super init];
    if (self) {
        _concurrentQueue = dispatch_queue_create("com.example.barrier", DISPATCH_QUEUE_CONCURRENT);
        _dataArray = [[NSMutableArray alloc] init];
    }
    return self;
}

// 读操作:可以并发执行
- (NSArray *)readData {
    __block NSArray *result;
    dispatch_sync(self.concurrentQueue, ^{
        result = [self.dataArray copy];
    });
    return result;
}

// 写操作:使用栅栏确保独占访问
- (void)writeData:(id)data {
    dispatch_barrier_async(self.concurrentQueue, ^{
        [self.dataArray addObject:data];
    });
}

@end

2. 调度组(Dispatch Group)

调度组用于监控一组任务的完成状态:

- (void)demonstrateDispatchGroup {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 方法1:使用dispatch_group_async
    dispatch_group_async(group, globalQueue, ^{
        NSLog(@"Task 1");
        [NSThread sleepForTimeInterval:2.0];
    });
    
    dispatch_group_async(group, globalQueue, ^{
        NSLog(@"Task 2");
        [NSThread sleepForTimeInterval:1.0];
    });
    
    // 方法2:使用dispatch_group_enter和dispatch_group_leave
    dispatch_group_enter(group);
    dispatch_async(globalQueue, ^{
        NSLog(@"Task 3");
        [NSThread sleepForTimeInterval:1.5];
        dispatch_group_leave(group);
    });
    
    // 等待所有任务完成
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"All tasks completed");
        // 更新UI或执行后续操作
    });
    
    // 或者同步等待(会阻塞当前线程)
    // dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}

3. 信号量(Semaphore)

信号量用于控制并发访问的资源数量:

@interface SemaphoreExample : NSObject
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@end

@implementation SemaphoreExample

- (instancetype)init {
    self = [super init];
    if (self) {
        // 创建信号量,允许最多3个并发访问
        _semaphore = dispatch_semaphore_create(3);
    }
    return self;
}

- (void)accessLimitedResource {
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(globalQueue, ^{
            // 等待信号量
            dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
            
            NSLog(@"Task %d accessing resource - Thread: %@", i, [NSThread currentThread]);
            [NSThread sleepForTimeInterval:2.0]; // 模拟资源使用
            NSLog(@"Task %d finished - Thread: %@", i, [NSThread currentThread]);
            
            // 释放信号量
            dispatch_semaphore_signal(self.semaphore);
        });
    }
}

@end

4. 一次性执行(dispatch_once)

确保代码块只执行一次,常用于单例模式:

+ (instancetype)sharedInstance {
    static id instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

NSOperation深度解析

NSOperation架构原理

NSOperation是基于GCD的高级抽象,提供了面向对象的多线程解决方案。NSOperation的核心组件包括:

1. NSOperation:抽象基类,定义了操作的基本接口 2. NSOperationQueue:操作队列,管理NSOperation的执行 3. NSBlockOperation:基于block的具体实现 4. NSInvocationOperation:基于方法调用的具体实现

NSOperation基本使用

// 1. 使用NSBlockOperation
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Block operation executed");
}];

// 添加额外的执行块
[blockOperation addExecutionBlock:^{
    NSLog(@"Additional execution block");
}];

// 2. 使用NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                  selector:@selector(performTask)
                                                                                    object:nil];

// 3. 创建操作队列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 3; // 设置最大并发数

// 4. 添加操作到队列
[operationQueue addOperation:blockOperation];
[operationQueue addOperation:invocationOperation];

自定义NSOperation

@interface CustomOperation : NSOperation
@property (nonatomic, strong) NSString *taskName;
@property (nonatomic, assign, getter=isExecuting) BOOL executing;
@property (nonatomic, assign, getter=isFinished) BOOL finished;
@end

@implementation CustomOperation

- (instancetype)initWithTaskName:(NSString *)taskName {
    self = [super init];
    if (self) {
        _taskName = taskName;
        _executing = NO;
        _finished = NO;
    }
    return self;
}

- (void)start {
    if (self.isCancelled) {
        self.finished = YES;
        return;
    }
    
    self.executing = YES;
    
    // 在后台线程执行任务
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self main];
    });
}

- (void)main {
    @autoreleasepool {
        if (self.isCancelled) {
            self.finished = YES;
            return;
        }
        
        // 执行实际任务
        NSLog(@"Executing task: %@", self.taskName);
        [NSThread sleepForTimeInterval:2.0];
        
        if (self.isCancelled) {
            self.finished = YES;
            return;
        }
        
        NSLog(@"Task completed: %@", self.taskName);
        
        // 标记完成
        self.executing = NO;
        self.finished = YES;
    }
}

- (void)setExecuting:(BOOL)executing {
    [self willChangeValueForKey:@"isExecuting"];
    _executing = executing;
    [self didChangeValueForKey:@"isExecuting"];
}

- (void)setFinished:(BOOL)finished {
    [self willChangeValueForKey:@"isFinished"];
    _finished = finished;
    [self didChangeValueForKey:@"isFinished"];
}

- (BOOL)isConcurrent {
    return YES; // 支持并发执行
}

@end

NSOperation依赖关系

NSOperation支持设置操作间的依赖关系:

- (void)demonstrateOperationDependencies {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建操作
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Operation 1 - Download data");
        [NSThread sleepForTimeInterval:2.0];
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Operation 2 - Process data");
        [NSThread sleepForTimeInterval:1.0];
    }];
    
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Operation 3 - Update UI");
    }];
    
    // 设置依赖关系:operation2依赖operation1,operation3依赖operation2
    [operation2 addDependency:operation1];
    [operation3 addDependency:operation2];
    
    // 添加到队列(顺序无关紧要,依赖关系会确保执行顺序)
    [queue addOperations:@[operation3, operation1, operation2] waitUntilFinished:NO];
}

NSOperationQueue高级特性

1. 队列控制

@interface OperationQueueManager : NSObject
@property (nonatomic, strong) NSOperationQueue *downloadQueue;
@property (nonatomic, strong) NSOperationQueue *processingQueue;
@end

@implementation OperationQueueManager

- (instancetype)init {
    self = [super init];
    if (self) {
        // 下载队列:串行执行
        _downloadQueue = [[NSOperationQueue alloc] init];
        _downloadQueue.maxConcurrentOperationCount = 1;
        _downloadQueue.name = @"Download Queue";
        
        // 处理队列:并发执行
        _processingQueue = [[NSOperationQueue alloc] init];
        _processingQueue.maxConcurrentOperationCount = 3;
        _processingQueue.name = @"Processing Queue";
        _processingQueue.qualityOfService = NSQualityOfServiceUserInitiated;
    }
    return self;
}

- (void)pauseAllOperations {
    [self.downloadQueue setSuspended:YES];
    [self.processingQueue setSuspended:YES];
}

- (void)resumeAllOperations {
    [self.downloadQueue setSuspended:NO];
    [self.processingQueue setSuspended:NO];
}

- (void)cancelAllOperations {
    [self.downloadQueue cancelAllOperations];
    [self.processingQueue cancelAllOperations];
}

@end

2. 操作优先级

- (void)demonstrateOperationPriority {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1; // 串行执行以观察优先级效果
    
    // 创建不同优先级的操作
    NSBlockOperation *lowPriorityOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Low priority operation");
    }];
    lowPriorityOp.queuePriority = NSOperationQueuePriorityLow;
    
    NSBlockOperation *normalPriorityOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Normal priority operation");
    }];
    normalPriorityOp.queuePriority = NSOperationQueuePriorityNormal;
    
    NSBlockOperation *highPriorityOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"High priority operation");
    }];
    highPriorityOp.queuePriority = NSOperationQueuePriorityHigh;
    
    // 添加到队列(高优先级的会先执行)
    [queue addOperations:@[lowPriorityOp, normalPriorityOp, highPriorityOp] waitUntilFinished:NO];
}

GCD vs NSOperation 对比分析

性能对比

GCD优势:

  1. 更轻量级:基于C语言,运行时开销更小
  2. 更快的任务调度:直接操作底层队列,调度效率更高
  3. 内存占用更少:没有额外的对象封装开销

NSOperation优势:

  1. 更丰富的功能:支持依赖关系、优先级、取消等高级特性
  2. 更好的可控性:可以暂停、恢复、取消操作
  3. 面向对象设计:更符合Objective-C的编程范式

使用场景选择

选择GCD的场景:

  1. 简单的异步任务执行
  2. 对性能要求极高的场景
  3. 需要使用GCD特有功能(如栅栏、信号量)
  4. 与C/C++代码集成

选择NSOperation的场景:

  1. 复杂的任务管理需求
  2. 需要任务间依赖关系
  3. 需要动态控制任务执行(暂停、恢复、取消)
  4. 需要监控任务状态和进度

多线程最佳实践

1. 线程安全策略

原子操作

@property (atomic, strong) NSString *threadSafeProperty;
@property (nonatomic, strong) NSString *nonThreadSafeProperty;

同步锁

@interface ThreadSafeCounter : NSObject
@property (nonatomic, assign) NSInteger count;
@end

@implementation ThreadSafeCounter {
    NSLock *_lock;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _lock = [[NSLock alloc] init];
    }
    return self;
}

- (void)increment {
    [_lock lock];
    _count++;
    [_lock unlock];
}

- (NSInteger)count {
    [_lock lock];
    NSInteger result = _count;
    [_lock unlock];
    return result;
}

@end

使用@synchronized

- (void)synchronizedMethod {
    @synchronized(self) {
        // 线程安全的代码块
        self.sharedResource = @"Updated value";
    }
}

2. 避免常见陷阱

主线程阻塞

// 错误:在主线程执行耗时操作
- (void)badExample {
    // 这会阻塞主线程,导致UI卡顿
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://example.com/data"]];
    self.label.text = @"Data loaded";
}

// 正确:在后台线程执行耗时操作
- (void)goodExample {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://example.com/data"]];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.label.text = @"Data loaded";
        });
    });
}

死锁避免

// 错误:可能导致死锁
- (void)deadlockExample {
    dispatch_sync(dispatch_get_main_queue(), ^{
        // 如果在主线程调用此方法,会导致死锁
        NSLog(@"This might cause deadlock");
    });
}

// 正确:检查当前线程
- (void)safeExample {
    if ([NSThread isMainThread]) {
        NSLog(@"Already on main thread");
    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"Switched to main thread");
        });
    }
}

3. 性能优化建议

合理设置并发数

// 根据任务类型设置合适的并发数
NSOperationQueue *cpuIntensiveQueue = [[NSOperationQueue alloc] init];
cpuIntensiveQueue.maxConcurrentOperationCount = [[NSProcessInfo processInfo] processorCount];

NSOperationQueue *ioIntensiveQueue = [[NSOperationQueue alloc] init];
ioIntensiveQueue.maxConcurrentOperationCount = 10; // IO密集型任务可以设置更高的并发数

避免过度创建线程

// 错误:每次都创建新队列
- (void)inefficientMethod {
    dispatch_queue_t queue = dispatch_queue_create("temp.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        // 执行任务
    });
}

// 正确:复用队列
@property (nonatomic, strong) dispatch_queue_t reusableQueue;

- (void)efficientMethod {
    if (!self.reusableQueue) {
        self.reusableQueue = dispatch_queue_create("reusable.queue", DISPATCH_QUEUE_CONCURRENT);
    }
    
    dispatch_async(self.reusableQueue, ^{
        // 执行任务
    });
}

总结

iOS多线程编程是构建高性能应用的基础技能。GCD提供了轻量级、高效的多线程解决方案,适合大多数并发编程需求。NSOperation则提供了更高级的抽象和更丰富的功能,适合复杂的任务管理场景。

在实际开发中,应该根据具体需求选择合适的技术方案,同时注意线程安全、避免死锁、合理控制并发数等最佳实践。掌握这些技术和原则,能够帮助开发者构建出响应迅速、性能优异的iOS应用。

记住,多线程编程的目标不仅仅是提升性能,更重要的是保证应用的稳定性和用户体验。在追求性能的同时,必须确保代码的正确性和可维护性。


上一篇     下一篇