OC中常用的多线程编程技术:
1. NSThread
NSThread是Objective-C中最基本的线程抽象,它允许程序员直接管理线程的生命周期。- NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil];
- [myThread start];
复制代码 使用NSThread时,必要本身管理线程的生命周期,包括创建、启动和销毁线程。这种方法给了开辟者很大的控制权,但也增长了复杂性,由于必要手动处理线程同步和线程安全问题。
2. Grand Central Dispatch (GCD)
GCD是一个强盛的基于C语言的API,它提供了一个并发执行任务的低级别方式。GCD使用任务队列和线程池来优化线程的使用。- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_async(queue, ^{
- // 异步执行的任务
- });
复制代码 GCD是保举的多线程编程方法之一,由于它的性能很好,而且简化了并发编程的复杂性(下一节详细介绍)。
3. Operation Queues
NSOperation和NSOperationQueue提供了一个面向对象的方式来执行并发操作。NSOperation是一个抽象类,可以通过继承它来定义详细的操作。- NSOperationQueue *queue = [[NSOperationQueue alloc] init];
- NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(myTaskMethod) object:nil];
- [queue addOperation:operation];
复制代码 NSOperationQueue可以管理多个NSOperation对象,它支持设置最大并发操作数和依赖关系。NSOperation比GCD更高级,它支持取消操作、设置操作依赖和观察操作状态。
4. Perform Selector Methods
Objective-C提供了performSelectornThread:等方法来在指定的线程上执行方法。- [self performSelector:@selector(myTaskMethod) onThread:myThread withObject:nil waitUntilDone:NO];
复制代码 这些方法简单易用,但它们不提供GCD和NSOperation的强盛功能。
5. POSIX Threads (pthreads)
POSIX线程是一套跨平台的线程相关的API,Objective-C可以直接使用这些API,由于它是C语言的超集。- #include <pthread.h>
- void *myThreadFunction(void *context) {
- // 线程执行的代码
- return NULL;
- }
- pthread_t thread;
- pthread_create(&thread, NULL, myThreadFunction, NULL);
复制代码 pthreads提供了很大的控制力,但它是一个低级别的API,通常不保举在OC中使用,除非必要与C库交互或有特殊的线程管理需求。
GCD的详细介绍
Grand Central Dispatch (GCD) 是 Apple 开辟的一个强盛的多核编程解决方案,它提供了一种简单且高效的方式来管理并发任务。GCD 使用任务(blocks of code)和队列(queues)的概念来执行工作。它是基于 C 语言实现的,可以在 Objective-C 和 Swift 中使用。
核心概念
任务
任务是指要执行的工作,通常是以 block 的情势提供。在 Objective-C 中,一个任务可以是一个 block 或者一个函数。
队列
队列是一种特殊的数据结构,用于按序次存储任务。GCD 提供了两种类型的队列:
- 串行队列(Serial Queue):一次只执行一个任务。队列中的任务按照添加的序次依次执行。在内部实现了一种机制,确保队列中的任务一次只能执行一个,并且按照它们被添加到队列中的序次来执行。这是通过队列管理和任务调理来实现的。
当将一个任务提交到串行队列时,这里是大致的工作流程:
- 任务排队:任务被添加到队列的末端。如果队列是空的,它会成为队列中的第一个任务。
- 任务执行:队列中的第一个任务(队头)被取出来执行。在这个任务执行期间,队列不会执行或者开始执行任何其他任务。
- 任务完成:一旦当前执行的任务完成,它会从队列中移除。
- 下一个任务:队列中的下一个任务(如今是队头)开始执行。
- 重复过程:这个过程会不停重复,直到队列中的所有任务都被执行完毕。
串行队列的关键特性是互斥执行,这意味着在任何给定时间点,队列中只有一个任务在执行。这种互斥是由GCD的底层调理机制保证的,它确保了即使在多核处理器上,串行队列上的任务也不会并行执行。
这种执行方式使得串行队列成为了同步执行任务的理想选择,特别是必要按序次执行一系列任务,而这些任务又不能同时执行时(例如,当任务必要按特定序次访问或修改共享资源时)。因此理论上一个线程就足够了。这是串行队列如何保证任务序次执行和互斥的关键。当一个任务在串行队列中开始执行时,它会持续运行直到完成,然后队列才会执行下一个任务。这个过程不必要同时有多个线程到场,由于不会有并行执行的情况。然而,实际上,由于GCD的工作原理,它可能会在内部使用多个线程来管理多个串行队列。GCD使用线程池来优化线程的使用,这意味着它会根据必要动态地为队列分配和接纳线程。但对于任何单一的串行队列来说,你可以认为它在任何时候都只在一个线程上执行任务。这种设计使得串行队列成为管理共享资源和避免并发问题的理想工具,由于它简化了同步和线程安全的需求。同时,它也淘汰了上下文切换的开销,由于任务是在单个线程上连续执行的。
- 并行队列(Concurrent Queue):可以同时执行多个任务。任务可以并发执行,但完成的序次可能会差别。
系统队列
GCD 提供了几种差别类型的系统队列:
- 主队列(Main Queue):串行队列,用于在主线程上执行任务,通常用于更新 UI。
- 全局队列(Global Queues):并行队列,有四个差别优先级的全局队列:高、默认、低和后台。
Grand Central Dispatch (GCD) 提供了几种差别类型的系统队列,这些队列是预先创建好的,可以直接使用。它们分为两大类:主队列(Main Queue)和全局队列(Global Queues)。
主队列(Main Queue)
- 主队列是一个特殊的串行队列,它在应用程序的主线程上执行任务。由于主线程通常用于更新UI,所以所有的UI更新都应该在主队列上执行,以确保UI的平滑和响应性。
- 使用dispatch_get_main_queue()函数可以获取主队列。
- dispatch_queue_t mainQueue = dispatch_get_main_queue();
- dispatch_async(mainQueue, ^{
- // 在主线程上更新UI
- });
复制代码 全局队列(Global Queues)
- 全局队列是并行队列,它们在后台执行任务,不会阻塞主线程。全局队列有四个差别的优先级:高(high)、默认(default)、低(low)和后台(background)。这些优先级对应于系统为任务分配的相对告急性。
- 使用dispatch_get_global_queue()函数可以获取全局队列,必要指定优先级和一个保存用的标记位(目前应该传递0)。
- dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_async(globalQueue, ^{
- // 在后台执行耗时任务
- });
复制代码 全局队列的优先级:
- 高优先级(DISPATCH_QUEUE_PRIORITY_HIGH):用于必要立刻执行的任务,但不应该阻塞主线程。
- 默认优先级(DISPATCH_QUEUE_PRIORITY_DEFAULT):用于大多数任务,如果没有特殊的优先级要求,应该使用这个优先级。
- 低优先级(DISPATCH_QUEUE_PRIORITY_LOW):用于不急迫的任务,可以等待其他更告急的任务完成后再执行。
- 后台优先级(DISPATCH_QUEUE_PRIORITY_BACKGROUND):用于那些用户不太可能立刻注意到的任务,如预取数据、维护或整理工作。
注意以下事项:
- 只管全局队列是并行队列,但是任务的启动序次仍然是按照它们被添加到队列的序次。
- 全局队列不保证任务完成的序次,任务可以并发执行。
- 主队列保证任务按照添加的序次一个接一个地执行。
- 在主队列上同步执行任务会导致死锁,由于主队列等待同步任务完成,而同步任务又在等待主队列可用,从而形成了相互等待的情况。
自定义队列
除了系统队列,GCD还允许创建自定义队列。自定义队列可以是串行的也可以是并行的。
- dispatch_queue_t serialQueue = dispatch_queue_create("com.example.mySerialQueue", DISPATCH_QUEUE_SERIAL);
复制代码- dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
复制代码 使用 GCD
异步执行
使用 dispatch_async 函数可以异步地将任务提交到队列中。这意味着它不会等待任务完成,而是立刻返回。- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_async(queue, ^{
- // 执行耗时的任务
- });
复制代码 同步执行
使用 dispatch_sync 函数可以同步地将任务提交到队列中。这会阻塞当前线程,直到任务执行完成。- dispatch_sync(queue, ^{
- // 执行任务
- });
复制代码 延迟执行
使用 dispatch_after 函数可以在指定的时间后异步执行任务。- 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秒后执行的任务
- });
复制代码 一次性执行
使用 dispatch_once 函数可以确保代码块只被执行一次,常用于创建单例。- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- // 只执行一次的代码
- });
复制代码 队列组
dispatch_group 允很多个任务作为一个组来提交,并在组中的所有任务完成时得到关照。- dispatch_group_t group = dispatch_group_create();
- dispatch_group_async(group, queue, ^{
- // 任务1
- });
- dispatch_group_async(group, queue, ^{
- // 任务2
- });
- dispatch_group_notify(group, dispatch_get_main_queue(), ^{
- // 所有任务完成后执行
- });
复制代码 信号量
dispatch_semaphore 用于控制访问资源的线程数量,可以用来实现线程同步。- dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
- dispatch_async(queue, ^{
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
- // 访问受限资源
- dispatch_semaphore_signal(semaphore);
- });
复制代码 注意事项
- 不要在串行队列上同步地执行任务,这可能会导致死锁。
- 尽量避免在主队列上同步执行耗时任务,这会阻塞 UI 更新。
- 使用 GCD 时,要注意内存管理,特别是在 block 中捕获外部变量时。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |