iOS开发-多线程编程
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供给了performSelector:onThread:
等办法来在指定的线程上履行办法。
[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 中捕获外部变量时。