1 多线程编程
-
NSThread
- 提供了更底层的线程控制能力,用于创建和管理线程对象。
- 需要手动管理线程的生命周期和线程通信。
-
GCD(Grand Central Dispatch)
- 基于 C 语言开发的一套用于多线程编程的 API。
- 自动管理线程的生命周期,包括任务调度、队列管理和线程管理。
-
NSOperation 和 NSOperationQueue
- 基于 GCD 的更高级别的抽象,提供了面向对象的方式来管理任务。
- 通过继承 NSOperation 类或使用它的子类 NSBlockOperation 来创建自定义操作。
- NSOperationQueue 是用来管理 NSOperation 对象的操作队列,系统会自动管理它们的执行顺序和线程分配。
- NSOperationQueue 和 NSOperation 提供了更高级别的任务管理机制,例如依赖关系、取消操作、优先级和同步等。
2 NSThread
2.1 创建线程
1. 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(todo) object:nil];
// 启动线程
[thread start];
2. 创建并自启动一个新的线程
[NSThread detachNewThreadSelector:@selector(todo) toTarget:self withObject:nil];
2.2 线程通信
#import <Foundation/Foundation.h>
@interface CustomThread : NSObject
@end
@implementation CustomThread
- (void)threadMethod:(id)object {
NSLog(@"子线程开始执行");
// 模拟耗时操作
[NSThread sleepForTimeInterval:2.0];
// 在主线程上调用方法,实现线程通信
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:YES];
NSLog(@"子线程结束执行");
}
- (void)updateUI {
NSLog(@"更新UI操作在主线程上执行");
}
@end
// 具体使用
CustomThread *customThread = [[CustomThread alloc] init];
NSThread *thread = [[NSThread alloc] initWithTarget:customThread selector:@selector(threadMethod:) object:nil];
[thread start];
3 GCD(Grand Central Dispatch)
3.1 任务和队列
- 任务的执行方式
- 同步:任务在当前线程中执行,会阻塞当前线程(不具备开启新线程的能力)
- 异步:任务在新的线程中执行,不会阻塞当前线程
- 队列的管理方式
- 串行:任务按顺序依次执行
- 并行:任务可以同时执行,不受顺序限制
- 任务和队列组合方式
- 同步并行:任务在当前线程中按顺序依次执行
- 异步串行:创建一条新线程,任务在新线程中按顺序依次执行
- 异步并行:创建多条新线程,任务同时执行
- 同步主队列:死锁
- 异步主队列:同异步串行,但不创建新线程,任务在主线程中按顺序依次执行
主队列是一个特殊的串行队列,通常用于在主线程上执行任务。
同步主队列:在主队列中执行的任务需要在主线程上执行,而同步任务会阻塞当前线程直到任务完成,所以会出现死锁问题。
异步主队列:系统会将任务放入一个全局共享的后台并发队列中,这个队列会自动管理任务的执行,而不需要专门创建新的线程。
3.2 创建任务和队列
1. 同步任务
dispatch_sync(queue, ^{
// TODO
});
2. 异步任务
dispatch_async(queue, ^{
// TODO
});
3. 串行队列
dispatch_queue_t queue = dispatch_queue_create("com.example.serialqueue", DISPATCH_QUEUE_SERIAL);
4. 并行队列
dispatch_queue_t queue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
5. 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue()
6. 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3.3 常用方法
1.dispatch_once:确保代码块在程序运行过程中只会执行一次,通常用于单例模式的实现。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只会执行一次的代码块
});
2.dispatch_after:延迟一段时间后执行任务。
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
dispatch_after(delay, dispatch_get_main_queue(), ^{
// 2秒后在主队列执行的任务
});
3. dispatch_apply:在指定的队列上多次执行指定的任务,可以用于遍历集合或执行重复性任务。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(5, queue, ^(size_t index) {
// 执行5次任务
});
4. dispatch_group_notify:当一组任务执行完成后,执行特定的任务。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
// Task 1
});
dispatch_group_async(group, queue, ^{
// Task 2
});
// 等待 dispatch group 中的所有任务完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
// Task 3
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 所有任务执行完成后在主队列执行的任务
NSLog(@"All tasks completed");
});
使用 dispatch_group_enter 和 dispatch_group_leave 动态地管理任务的数量。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 往 dispatch group 中添加任务
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"Task 1 started");
// 模拟耗时任务
sleep(2);
NSLog(@"Task 1 finished");
// 任务完成后离开 dispatch group
dispatch_group_leave(group);
});
// 往 dispatch group 中添加另一个任务
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"Task 2 started");
// 模拟耗时任务
sleep(3);
NSLog(@"Task 2 finished");
// 任务完成后离开 dispatch group
dispatch_group_leave(group);
});
// 等待所有任务完成
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"All tasks have completed");
// 在这里可以进行一些操作,例如更新 UI 界面
});
5. dispatch_barrier_async:在并发队列中插入一个栅栏,确保在它之前提交的任务执行完成后再执行,但是在它之后提交的任务需要等待它执行完成。
dispatch_queue_t queue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // Task 1
});
dispatch_async(queue, ^{ // Task 2
});
dispatch_barrier_async(queue, ^{ // Barrier task
// 确保在此之前的任务执行完成后才执行,之后的任务需要等待
});
dispatch_async(queue, ^{ // Task 3
});
dispatch_async(queue, ^{ // Task 4
});
6. dispatch_semaphore_create 和 dispatch_semaphore_wait:使用信号量实现线程同步。
// 创建一个初始值为0的信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Task A started");
// 模拟耗时任务
sleep(2);
NSLog(@"Task A finished");
// 发出信号量,允许 Task B 执行
dispatch_semaphore_signal(semaphore);
});
NSLog(@"Task B waiting");
// 等待信号量
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Task B started");
// 模拟耗时任务
sleep(2);
NSLog(@"Task B finished");
});
3.4 线程通信
- (void)communication {
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 异步执行任务
dispatch_async(queue, ^{
// 模拟耗时操作
for (int i = 0; i < 2; i++) {
// TODO
}
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程更新 UI
});
});
}
4 NSOperation 和 NSOperationQueue
4.1 NSOperation
NSOperation 是 Foundation 框架中的一个抽象类,不能直接使用,可以通过其子类 NSBlockOperation 和 NSInvocationOperation 来创建操作,或者自定义一个继承自 NSOperation 的子类。
1. NSBlockOperation
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
// 执行操作
}];
[operation addExecutionBlock:^{
// 添加额外的执行操作任务,同一个操作,两个任务
}];
// 执行操作,默认同步执行
[operation start];
NSOperation 调用
start方法默认是同步执行,会在当前线程中执行任务,而不会开启新的线程。
NSOperation 对象添加到 NSOperationQueue 中执行,系统会自动异步执行操作,无需手动调用start方法。
NSBlockOperation 封装的操作数量大于 1 时,无论是否使用 NSOperationQueue,NSBlockOperation 都会以并发的方式异步执行这些操作。
2. NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(runOperation) object:nil];
[operation start];
3. 自定义子类
@interface MyCustomOperation : NSOperation
// 可以添加自定义的属性,用于传递参数等
@property (readonly, getter=isCancelled) BOOL cancelled;
@end
@implementation MyCustomOperation
- (void)main {
@autoreleasepool {
// 在这里执行任务逻辑
// 检查是否被取消
if (self.isCancelled) {
return;
}
// 执行任务...
// 完成任务后,可以通过调用完成的代码块或通知等方式来通知其他部分
}
}
@end
4.2 NSOperationQueue
NSOperationQueue 是一个用于管理 NSOperation 对象的操作队列。将 NSOperation 对象添加到 NSOperationQueue 中,可以实现对操作的异步执行、并发控制、操作依赖管理等功能。
- 异步执行: 将 NSOperation 添加到 NSOperationQueue 中后,系统会自动在合适的时机异步执行这些操作,无需手动调用 start 方法。NSOperationQueue 会根据队列的类型(串行队列或并发队列)来确定操作的执行方式。
- 并发控制: NSOperationQueue 可以用来控制操作的并发数量,可以创建串行队列(NSOperationQueue.serial)实现串行执行,也可以创建并发队列(NSOperationQueue.concurrent)实现并发执行。
- 操作依赖管理: 可以通过添加操作依赖关系(
addDependency:)来确保某些操作在其他操作执行完毕后再执行,NSOperationQueue 会自动处理依赖关系,保证操作的执行顺序满足依赖关系。 - 线程管理: NSOperationQueue 会自动管理执行操作所需的线程,无需手动创建和管理线程。
1. 操作队列类型
主队列:主队列是一个全局的操作队列,用于在主线程上执行操作。
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
自定义队列:通过创建自定义的 NSOperationQueue 对象来管理操作。
NSOperationQueue *customQueue = [[NSOperationQueue alloc] init];
2. 操作队列方法
addOperation:向队列中添加操作addOperations:waitUntilFinished:添加多个操作,并选择是否等待它们全部执行完毕addOperationWithBlock:添加一个使用 block 来定义的操作cancelAllOperations取消队列中的所有操作,操作对象的cancel方法可以取消单个操作setSuspended:暂停和恢复队列
3. 操作依赖管理
// 创建 NSOperationQueue 对象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建两个自定义操作
NSOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
// 执行操作1的任务
}];
NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
// 执行操作2的任务
}];
// 将操作2添加为操作1的依赖,操作2依赖于操作1,因此只有在操作1完成后,操作2才能执行
[operation2 addDependency:operation1];
// 将操作添加到操作队列中执行
[queue addOperation:operation1];
[queue addOperation:operation2];
4. 如何取消任务执行?
调用操作对象 cancel 方法并不能使操作马上停止执行。
当 NSOperation 的 cancel 方法被调用后,如果操作不在队列中,这个方法会将操作的 isFinished 设为YES,如果在操作队列中,这个方法会将操作对象的 isCancelled 状态设为YES,并且 isReady 设为YES,让队列调用它的 start 方法。在 start 或者 main 方法实现中,我们应该检查 isCancelled 和 isFinished 属性,如果任意一个为YES,就不执行操作,直接返回,如果是并发操作,让 isFinished 方法返回YES,如果是非并发操作,设置isFinished 值为YES。