NSThread
NSThread
是 iOS 和 macOS 中用于多线程编程的类。它封装了线程的创建和管理,允许开发者在后台执行任务而不阻塞主线程。这样可以保持应用界面的响应性,同时执行如下载文件、数据处理等耗时操作。
使用 NSThread 的常用方法和属性:
detachNewThreadSelector:toTarget:withObject:
: 创建一个新的线程并开始执行指定的方法。initWithTarget:selector:object:
: 初始化一个线程对象,你需要手动调用start
方法来启动线程。start
: 启动线程,开始执行初始化时指定的方法。isMainThread
: 判断当前线程是否是主线程。mainThread
: 获取主线程。currentThread
: 获取当前线程。
创建线程的几种方式:
在 Objective-C 中,有几种不同的方式来创建和使用 NSThread
,以在新线程上执行任务。以下是创建 NSThread
的几种常用方法:
1. 使用 detachNewThreadSelector:toTarget:withObject: 方法
这种方式会创建一个新的线程并立即启动它。你需要指定一个选择器(selector),它是当前类的一个方法,这个方法将在新线程上执行。
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:someArgument];
2. 使用 initWithTarget:selector:object: 方法
这种方式允许你创建一个 NSThread
对象,但是它不会立即启动。你可以设置线程对象的属性,然后调用 start
方法来启动线程。
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:someArgument];
[myThread start]; // 手动启动线程
3. 创建并自动启动线程
你可以创建一个继承自 NSThread
的子类,并重写 main
方法。当你创建这个子类的实例并调用 start
方法时,main
方法将在新线程上执行。
@interface MyThread : NSThread
@end
@implementation MyThread
- (void)main {
// 在这里执行线程的任务
}
@end
// 使用
MyThread *myThread = [[MyThread alloc] init];
[myThread start]; // 启动线程
4. 使用 performSelectorInBackground:withObject: 方法
这个方法会在当前对象上创建并启动一个新线程,并在这个新线程上调用指定的选择器(selector)。这是执行后台任务的简便方式,但不提供对线程对象的直接访问。
[self performSelectorInBackground:@selector(myThreadMainMethod:) withObject:someArgument];
示例:线程执行的方法
无论使用哪种方法启动线程,你都需要提供一个方法来执行线程的任务。这个方法的签名通常如下:
- (void)myThreadMainMethod:(id)argument {
// 执行线程任务
}
在这个方法中,你可以执行任何长时间运行的任务,例如数据处理、文件读写等。
注意事项:
- 线程安全:在使用
NSThread
时,你需要确保代码是线程安全的,特别是在访问共享资源时。 - 主线程更新 UI:记住始终在主线程上更新 UI,你可以使用
performSelectorOnMainThread:withObject:waitUntilDone:
方法来安排在主线程上执行的操作。 - 更现代的替代方案:尽管
NSThread
提供了一定的灵活性,但现代 iOS 开发中推荐使用Grand Central Dispatch (GCD)
或NSOperationQueue
来实现并发编程,因为它们提供了更简单、更强大的并发处理能力,并帮助你避免直接管理线程的复杂性。
GCD
Grand Central Dispatch (GCD) 是 Apple 开发的一种基于 C 语言的轻量级多线程解决方案。它利用应用程序运行的多核处理器优势,自动和异步地执行任务。GCD 提供了一个易于使用的并发模型,而不是传统线程的创建、同步和管理方式。
GCD 的主要特性:
- 任务分派:可将任务异步或同步分派到主线程或多个后台线程上执行。
- 队列:任务在
dispatch_queue_t
对象(简称队列)中执行,队列保证了任务的执行顺序。队列分为两种类型:串行队列和并发队列。 - 管理并发:GCD 自动管理线程池,无需手动创建或销毁线程。
创建和使用队列:
- 串行队列:一个时间点只执行一个任务。任务按照被添加到队列的顺序依次执行。
- 并发队列:可以同时执行多个任务。任务可以以任何顺序开始,但仍然按照被添加到队列的顺序完成。
GCD的常用函数:
dispatch_async
:异步地将任务分派到指定的队列中执行,不会阻塞当前线程。dispatch_sync
:同步地将任务分派到指定的队列中执行,会阻塞当前线程直到任务完成。dispatch_after
:在指定的延迟时间之后异步地将任务分派到指定的队列中执行。dispatch_once
:保证代码块在应用的生命周期中只执行一次。dispatch_group
:管理一组任务的完成,可以在所有任务完成之后收到通知。
示例代码(Objective-C):
// 获取全局并发队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 异步执行任务
dispatch_async(globalQueue, ^{
// 在后台执行的任务
NSLog(@"后台执行任务");
// 回到主线程更新 UI
dispatch_async(dispatch_get_main_queue(), ^{
// 在主线程执行的任务
NSLog(@"回到主线程更新 UI");
});
});
// 延迟执行任务
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
// 2 秒后在主线程执行的任务
NSLog(@"延迟执行的任务");
});
// 执行一次代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行一次的代码
NSLog(@"只执行一次的任务");
});
此处知识简单整理,建议看看:
https://juejin.cn/post/6844903566398717960
NSOperation 和 NSOperationQueue
NSOperation
和 NSOperationQueue
是一套强大的 API,是对GCD的更高一层的封装。
NSOperation
NSOperation
是一个抽象类,它封装了要在后台执行的单个任务。你不能直接使用 NSOperation
,而是应该使用它的两个具体子类 NSBlockOperation
或 NSInvocationOperation
,或者创建自定义的 NSOperation
子类。
- NSBlockOperation: 你可以通过给
NSBlockOperation
添加一个或多个执行块来创建操作。如果添加了多个块,那么这些块可以并发执行。 - 自定义 NSOperation: 通过创建
NSOperation
的子类,并重写main
或者start
方法,你可以实现自定义的并发或非并发操作。
NSOperationQueue
NSOperationQueue
是一个操作队列,它管理一组 NSOperation
对象。它负责调度这些操作对象的执行,可以是串行的也可以是并行的,并且可以设置优先级和依赖关系。
- 主队列(Main Queue): 用于在主线程上调度操作,通常用于更新 UI。
- 自定义队列: 用于在后台执行操作,可以是并发或串行执行。
关键特性
- 依赖关系(Dependencies): 你可以设置操作之间的依赖关系,以保证它们按照特定的顺序执行。
- 优先级(Priority): 可以为
NSOperation
设置优先级,以影响其在队列中的执行顺序。 - 取消(Cancellation):
NSOperation
提供了取消机制,可以取消尚未执行的操作。 - 暂停(Suspending)和恢复(Resuming):
NSOperationQueue
可以被暂停和恢复,这对于控制队列执行非常有用。
示例使用:
创建自定义 NSOperation
子类:
@interface MyOperation : NSOperation
@end
@implementation MyOperation
- (void)main {
if ([self isCancelled]) {
return;
}
// 执行长时间运行的任务...
}
@end
创建 NSOperationQueue
并添加操作:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 使用 NSBlockOperation 创建操作
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务");
}];
// 添加操作到队列
[queue addOperation:blockOperation];
// 创建并添加自定义操作
MyOperation *myOperation = [[MyOperation alloc] init];
[queue addOperation:myOperation];
设置操作之间的依赖关系:
NSOperation *firstOperation = [[NSOperation alloc] init];
NSOperation *secondOperation = [[NSOperation alloc] init];
[secondOperation addDependency:firstOperation]; // secondOperation 将等待 firstOperation 完成
[queue addOperation:firstOperation];
[queue addOperation:secondOperation];
暂停和恢复操作队列:
[queue setSuspended:YES]; // 暂停队列
[queue setSuspended:NO]; // 恢复队列
取消操作或操作队列中的所有操作:
[myOperation cancel]; // 取消单个操作
[queue cancelAllOperations]; // 取消队列中的所有操作
https://xiaozhuanlan.com/topic/2037951486
线程同步
1. NSLock
NSLock
是一个提供基本锁功能的对象,用于在访问共享资源时防止多个线程同时访问。
NSLock *lock = [[NSLock alloc] init];
[lock lock]; // 加锁
// 访问或修改共享资源
[lock unlock]; // 解锁
2. @synchronized
@synchronized
是一个 Objective-C 特有的构造,它会自动为代码块加锁和解锁。
@synchronized(self) {
// 访问或修改共享资源
}
3. NSRecursiveLock
NSRecursiveLock
允许同一线程多次获得相同的锁。它对于需要递归地访问共享资源的场景很有用。
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
[recursiveLock lock]; // 加锁
// 递归访问共享资源
[recursiveLock unlock]; // 解锁
4. NSCondition
NSCondition
是一个条件锁,它允许线程在某些条件下暂停执行,并在条件满足时被唤醒。
NSCondition *condition = [[NSCondition alloc] init];
[condition lock];
while (!conditionIsTrue) {
[condition wait]; // 等待条件满足
}
// 访问或修改共享资源
[condition signal]; // 唤醒一个等待的线程
[condition unlock];
5. Semaphores(信号量)
信号量是 GCD 提供的一种同步机制,用于限制同时访问共享资源的线程数。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量
// 访问或修改共享资源
dispatch_semaphore_signal(semaphore); // 发送信号量
6. Dispatch Queues(调度队列)
使用 GCD 的串行调度队列可以保证任务按顺序执行,从而实现线程安全。
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
// 访问或修改共享资源
});
7. Dispatch Barrier(调度屏障)
当使用并发队列时,可以使用调度屏障来确保屏障之前提交的任务都完成后才执行屏障中的任务。
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_barrier_async(concurrentQueue, ^{
// 访问或修改共享资源
});
8. Dispatch Group(调度组)
调度组用于同步一组任务的完成。
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, concurrentQueue, ^{
// 执行任务
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 当前面的任务都完成后,在主线程上执行
});
9. Atomic Properties(原子属性)
在 Objective-C 中,通过在属性声明中使用 atomic
关键字(默认),可以保证属性的原子性访问。
@property (atomic, strong) NSObject *myAtomicObject;
10. Read-Write Lock(读写锁)
在某些情况下,你可能需要实现读写锁逻辑,允许多个读操作同时进行,但写操作必须独占访问。你可以通过 pthread_rwlock_t
或使用 GCD 的 dispatch_barrier_async
来实现。
https://www.jianshu.com/p/8be90bc4e853