当前位置:   article > 正文

[iOS]进程-线程-队列-任务

[iOS]进程-线程-队列-任务

一、进程(Process)

iOS 开发中,进程是一个基本的概念,虽然通常作为开发者,你不需要像在某些其他操作系统那样进行直接的进程管理,因为 iOS 提供了很多高级别的抽象。不过,了解进程的概念对于理解应用程序的运行环境仍然很重要。

1.进程定义

进程是一个执行中的程序的实例。它是系统资源(如 CPU 时间、内存空间、文件描述符等)分配的基本单位。每个进程都运行在其自己的隔离空间内,操作系统负责管理进程的生命周期以及它们对系统资源的使用。

2.iOS进程特点

沙盒环境

出于安全考虑,iOS 上的每个应用程序都运行在一个称为“沙盒”的隔离环境中。这意味着应用程序只能访问分配给自己的文件系统目录,并受限于对设备其他部分的访问。这有助于防止恶意软件破坏系统或窃取数据。

单进程模型

iOS 应用通常作为单个进程运行。尽管应用可以启动多个线程,但它们都是属于同一个进程的。

生命周期管理

iOS 操作系统严格管理应用进程的生命周期。应用程序状态(如启动、挂起、恢复、终止)由系统根据资源需求和用户行为管理。

后台执行限制

为了保持电池寿命和系统性能,iOS 对后台进程的执行有严格的限制。应用程序通常情况下在退到后台后会被挂起,除非它们声明了后台执行的任务。

内存管理

iOS 使用引用计数(如 ARC - Automatic Reference Counting)来管理内存。当系统内存不足时,它会自动终止后台进程来释放资源。

接口限制

iOS 应用程序不能直接调用大多数系统调用,而是通过 iOS SDK 提供的 API 与操作系统交互。这些 API 为进程间通信、资源访问等提供了接口。

3.进程间通信

在 iOS 中,进程间通信被限制和控制,但仍然提供了几种机制:

  • URL Schemes:应用可以注册 URL schemes 来与其他应用交互。
  • UIActivityViewController:允许用户在不同的应用之间分享内容。
  • Universal Links:通过网页链接直接打开应用,提供更加无缝的体验。
  • App Extensions:允许应用在某些受限的环境中(如通知中心、键盘扩展等)提供功能。
  • Handoff:支持在不同设备的同一应用之间继续任务。
  • 共享容器:允许同一开发者的多个应用共享数据。

二、线程(Thread)

在iOS开发中,线程是一个比进程更轻量级的执行单元。一个进程内可以有多个线程,它们共享进程的内存空间和资源,但是每个线程拥有自己的执行序列、栈空间和线程状态。线程可以并行或并发地执行多个任务,从而提高应用程序的效率和响应性。

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

1.线程的基本概念

线程隔离

每个线程都有自己的私有栈空间,执行上下文和程序计数器。

创建成本

线程的创建和上下文切换比进程轻量得多。

资源共享

同一进程下的线程共享进程的内存和资源。

同步机制

为了防止数据冲突,通常需要通过锁、信号量等同步机制来控制线程对共享资源的访问。

2.iOS中的线程管理

iOS提供了几种不同的方式来管理和使用线程:

(1).POSIX Threads (pthreads)

这是一种跨平台的线程API,允许更精细的线程控制。但它不提供自动内存管理,并且使用起来比较复杂。

Swift:

  1. import Foundation
  2. // 线程执行的函数
  3. func threadFunction(arg: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
  4. print("Thread is now running")
  5. // 执行一些任务
  6. print("Thread finishing")
  7. return nil
  8. }
  9. // 创建线程
  10. var thread = pthread_t(bitPattern: 0)
  11. var attribute = pthread_attr_t()
  12. pthread_attr_init(&attribute)
  13. pthread_create(&thread, &attribute, { arg in
  14. return threadFunction(arg: arg)
  15. }, nil)
  16. pthread_attr_destroy(&attribute)
  17. // 等待线程结束
  18. pthread_join(thread!, nil)

OC:

  1. #import <Foundation/Foundation.h>
  2. #import <pthread.h>
  3. void *threadFunction(void *arg) {
  4. NSLog(@"Thread is now running");
  5. // 执行一些任务
  6. NSLog(@"Thread finishing");
  7. return NULL;
  8. }
  9. int main(int argc, const char * argv[]) {
  10. @autoreleasepool {
  11. pthread_t thread;
  12. pthread_attr_t attribute;
  13. pthread_attr_init(&attribute);
  14. pthread_create(&thread, &attribute, threadFunction, NULL);
  15. pthread_attr_destroy(&attribute);
  16. // 等待线程结束
  17. pthread_join(thread, NULL);
  18. }
  19. return 0;
  20. }

(2).NSThread

NSThread是Objective-C提供的面向对象的线程操作方式。它相对于pthreads简单一些,但仍然需要开发者手动管理线程的生命周期和同步。

Swift:

  1. import Foundation
  2. // 线程调用的方法
  3. @objc class ThreadExample: NSObject {
  4. @objc func threadMethod() {
  5. print("Thread is now running")
  6. // 执行一些任务
  7. print("Thread finishing")
  8. }
  9. }
  10. // 创建并启动线程
  11. let example = ThreadExample()
  12. let thread = Thread(target: example, selector: #selector(ThreadExample.threadMethod), object: nil)
  13. thread.start()
  14. // 主线程继续执行其它任务
  15. print("Main thread is not blocked")

OC:

  1. #import <Foundation/Foundation.h>
  2. @interface ThreadExample : NSObject
  3. - (void)threadMethod;
  4. @end
  5. @implementation ThreadExample
  6. - (void)threadMethod {
  7. @autoreleasepool {
  8. NSLog(@"Thread is now running");
  9. // 执行一些任务
  10. NSLog(@"Thread finishing");
  11. }
  12. }
  13. @end
  14. int main(int argc, const char * argv[]) {
  15. @autoreleasepool {
  16. ThreadExample *example = [[ThreadExample alloc] init];
  17. NSThread *thread = [[NSThread alloc] initWithTarget:example selector:@selector(threadMethod) object:nil];
  18. [thread start];
  19. // 主线程继续执行其它任务
  20. NSLog(@"Main thread is not blocked");
  21. }
  22. return 0;
  23. }

(3).Grand Central Dispatch (GCD)

GCD是Apple推荐的多线程解决方案,它使用队列来抽象线程的创建和管理。GCD可以自动管理线程池,优化线程数量,并提供了简单的API来执行异步任务。

Swift:

  1. import Foundation
  2. // 创建串行队列
  3. let serialQueue = DispatchQueue(label: "com.example.serialQueue")
  4. // 创建并发队列
  5. let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
  6. // 在串行队列上异步执行任务
  7. serialQueue.async {
  8. for i in 1...3 {
  9. print("Serial Queue Async Task \(i)")
  10. }
  11. }
  12. // 在并发队列上异步执行任务
  13. concurrentQueue.async {
  14. for i in 1...3 {
  15. print("Concurrent Queue Async Task \(i)")
  16. }
  17. }
  18. // 在主队列上延迟执行任务
  19. DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
  20. print("This task is run on the main queue after 2 seconds")
  21. }
  22. // 使用全局并发队列
  23. DispatchQueue.global().async {
  24. print("Global queue async task")
  25. }
  26. // 主线程继续执行其他任务
  27. print("Main thread is not blocked")

下面示例代码的打印顺序?

  1. override func viewDidLoad() {
  2. super.viewDidLoad()
  3. DispatchQueue.main.async {
  4. print("1")
  5. }
  6. print("2")
  7. // 创建串行队列
  8. let serialQueue = DispatchQueue(label: "com.example.serialQueue")
  9. // 在串行队列上异步执行任务
  10. serialQueue.async {
  11. print("3")
  12. }
  13. print("4")
  14. // 创建并发队列
  15. let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
  16. // 在并发队列上异步执行任务
  17. concurrentQueue.async {
  18. print("5")
  19. }
  20. print("6")
  21. DispatchQueue.global().sync {
  22. print("7")
  23. }
  24. print("8")
  25. // 获取一个后台队列(优先级为默认)
  26. let backgroundQueue = DispatchQueue.global(qos: .background)
  27. // 在后台队列上异步执行任务
  28. backgroundQueue.async {
  29. // 放置后台线程需要执行的任务
  30. print("9")
  31. }
  32. print("10")
  33. DispatchQueue.global().async {
  34. print("11")
  35. }
  36. print("12")
  37. }
  • DispatchQueue.main.async { print("1") } 这个异步调用将任务排队到主队列,但是它不会立即执行。由于当前代码块已经在主线程上执行(viewDidLoad),所以这个异步任务将会在当前代码块完成后的某个时间点执行。
  • serialQueue.async { print("3") } 在一个自定义的串行队列上异步执行。它将会在某个不确定的时间点执行,因为它不会阻塞当前线程。但由于它是串行队列,它会保证按照被添加到队列中的顺序执行。
  • concurrentQueue.async { print("5") } 在一个自定义并发队列上异步执行。它可能几乎立即开始执行,但实际的执行时间取决于系统的调度。
  • DispatchQueue.global().sync { print("7") } 在全局并发队列上同步执行,这将会阻塞主线程,直到这个同步任务完成,所以 print("7") 会在 print("6") 之后,而且在 print("8") 之前执行。
  • backgroundQueue.async { print("9") } 在一个全局队列上异步执行,这个队列的优先级是后台。它将在未来某个不确定的时间点执行,不会阻塞主线程。但由于QoS 级别是一个提示给系统的优先级标识,用于决定哪些任务应该先执行。.background QoS 是最低的优先级,所以执行顺序会靠后。
  • DispatchQueue.global().async { print("11") } 在全局并发队列上异步执行,它会在未来某个不确定的时点执行。

综上所述,同步执行的 print 调用将按顺序立即打印,按照 2, 4, 6, 7, 8, 10, 12 的顺序。异步派发的任务,print("1")确定会在最后打印. 其它print("3")print("5")print("9"), 和 print("11") 将在未来某个时点执行,它们的执行顺序取决于系统对任务的调度和队列的类型(串行或并发), 但print("9")级别最低会在最后执行。因此,其他异步任务的执行顺序是不确定的。

实际打印顺序为:

2/4/3/6/5/7/8/10/12/11/9/1

也打印成这种:

2/4/6/7/8/3/5/10/12/11/9/1

也会打印成这种 :

2/4/6/3/5/7/8/10/12/11/9/1

OC:

  1. #import <Foundation/Foundation.h>
  2. int main(int argc, const char * argv[]) {
  3. @autoreleasepool {
  4. // 创建串行队列
  5. dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
  6. // 创建并发队列
  7. dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
  8. // 在串行队列上异步执行任务
  9. dispatch_async(serialQueue, ^{
  10. for (int i = 1; i <= 3; i++) {
  11. NSLog(@"Serial Queue Async Task %d", i);
  12. }
  13. });
  14. // 在并发队列上异步执行任务
  15. dispatch_async(concurrentQueue, ^{
  16. for (int i = 1; i <= 3; i++) {
  17. NSLog(@"Concurrent Queue Async Task %d", i);
  18. }
  19. });
  20. // 在主队列上延迟执行任务
  21. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  22. NSLog(@"This task is run on the main queue after 2 seconds");
  23. });
  24. // 使用全局并发队列
  25. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  26. NSLog(@"Global queue async task");
  27. });
  28. // 主线程继续执行其他任务
  29. NSLog(@"Main thread is not blocked");
  30. }
  31. return 0;
  32. }

(4).Operation Queues

Operation Queues是基于GCD但更高层次的抽象。它使用NSOperation对象来表示单个任务,可以设置任务的依赖关系,并通过NSOperationQueue来管理任务的执行。

Swift:

  1. import Foundation
  2. // 创建一个操作队列
  3. let operationQueue = OperationQueue()
  4. // 创建一个NSBlockOperation来执行任务
  5. let operation1 = BlockOperation {
  6. print("Operation 1 is being executed")
  7. // 模拟一些工作
  8. Thread.sleep(forTimeInterval: 2)
  9. print("Operation 1 has finished executing")
  10. }
  11. // 创建另一个操作
  12. let operation2 = BlockOperation {
  13. print("Operation 2 is being executed")
  14. // 模拟一些工作
  15. Thread.sleep(forTimeInterval: 1)
  16. print("Operation 2 has finished executing")
  17. }
  18. // 添加操作依赖,operation2将会在operation1完成后才开始执行
  19. operation2.addDependency(operation1)
  20. // 将操作添加到队列中
  21. operationQueue.addOperation(operation1)
  22. operationQueue.addOperation(operation2)
  23. // 添加一个操作到队列中,使用addOperation的闭包形式
  24. operationQueue.addOperation {
  25. print("Operation 3 is being executed")
  26. // 执行一些工作
  27. print("Operation 3 has finished executing")
  28. }
  29. // 主线程继续执行其他任务
  30. print("Main thread is not blocked")

OC:

  1. #import <Foundation/Foundation.h>
  2. int main(int argc, const char * argv[]) {
  3. @autoreleasepool {
  4. // 创建操作队列
  5. NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
  6. // 创建NSBlockOperation来执行任务
  7. NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
  8. NSLog(@"Operation 1 is being executed");
  9. // 模拟一些工作
  10. [NSThread sleepForTimeInterval:2];
  11. NSLog(@"Operation 1 has finished executing");
  12. }];
  13. // 创建另一个操作
  14. NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
  15. NSLog(@"Operation 2 is being executed");
  16. // 模拟一些工作
  17. [NSThread sleepForTimeInterval:1];
  18. NSLog(@"Operation 2 has finished executing");
  19. }];
  20. // 添加操作依赖,operation2将会在operation1完成后才开始执行
  21. [operation2 addDependency:operation1];
  22. // 将操作添加到队列中
  23. [operationQueue addOperation:operation1];
  24. [operationQueue addOperation:operation2];
  25. // 使用addOperationWithBlock添加另一个操作到队列中
  26. [operationQueue addOperationWithBlock:^{
  27. NSLog(@"Operation 3 is being executed");
  28. // 执行一些工作
  29. NSLog(@"Operation 3 has finished executing");
  30. }];
  31. // 主线程继续执行其他任务
  32. NSLog(@"Main thread is not blocked");
  33. }
  34. return 0;
  35. }

3.线程的同步

由于线程共享内存和资源,同步变得非常重要。

iOS提供了多种同步机制来避免数据竞争和条件竞争:

(1).锁(Locks)

如NSLock、NSRecursiveLock、NSCondition等,用于控制对共享资源的访问。

Swift:

  1. import Foundation
  2. var sharedResource = [String]()
  3. let lock = NSLock()
  4. func accessSharedResource() {
  5. lock.lock()
  6. // 安全地访问共享资源
  7. sharedResource.append("Data")
  8. lock.unlock()
  9. }
  10. // 在不同的线程中调用accessSharedResource()
  11. DispatchQueue.global().async {
  12. accessSharedResource()
  13. }
  14. DispatchQueue.global().async {
  15. accessSharedResource()
  16. }

OC:

  1. #import <Foundation/Foundation.h>
  2. NSMutableArray *sharedResource = [NSMutableArray array];
  3. NSLock *lock = [[NSLock alloc] init];
  4. void accessSharedResource() {
  5. [lock lock];
  6. // 安全地访问共享资源
  7. [sharedResource addObject:@"Data"];
  8. [lock unlock];
  9. }
  10. // 在不同的线程中调用accessSharedResource()
  11. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  12. accessSharedResource();
  13. });
  14. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  15. accessSharedResource();
  16. });

(2).信号量(Semaphores)

GCD中的dispatch_semaphore_t可以用来控制对资源的并发访问。

Swift:

  1. import Foundation
  2. let semaphore = DispatchSemaphore(value: 1)
  3. func accessSharedResourceWithSemaphore() {
  4. semaphore.wait() // 等待信号量
  5. // 安全地访问共享资源
  6. sharedResource.append("Data")
  7. semaphore.signal() // 释放信号量
  8. }
  9. // 在不同的线程中调用accessSharedResourceWithSemaphore()
  10. DispatchQueue.global().async {
  11. accessSharedResourceWithSemaphore()
  12. }
  13. DispatchQueue.global().async {
  14. accessSharedResourceWithSemaphore()
  15. }

OC:

  1. #import <Foundation/Foundation.h>
  2. dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
  3. void accessSharedResourceWithSemaphore() {
  4. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量
  5. // 安全地访问共享资源
  6. [sharedResource addObject:@"Data"];
  7. dispatch_semaphore_signal(semaphore); // 释放信号量
  8. }
  9. // 在不同的线程中调用accessSharedResourceWithSemaphore()
  10. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  11. accessSharedResourceWithSemaphore();
  12. });
  13. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  14. accessSharedResourceWithSemaphore();
  15. });

(3).原子操作

OSAtomic系列函数,提供了简单的原子性操作。

  1. #import <libkern/OSAtomic.h>
  2. __block volatile int32_t counter = 0;
  3. void incrementCounter() {
  4. OSAtomicIncrement32(&counter);
  5. }
  6. // 在不同的线程中调用incrementCounter()
  7. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  8. incrementCounter();
  9. });
  10. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  11. incrementCounter();
  12. });

(4).@synchronized

Objective-C中的关键字,可以简化锁的使用。

  1. #import <Foundation/Foundation.h>
  2. void accessSharedResourceWithSynchronized() {
  3. @synchronized(sharedResource) {
  4. // 安全地访问共享资源
  5. [sharedResource addObject:@"Data"];
  6. }
  7. }
  8. // 在不同的线程中调用accessSharedResourceWithSynchronized()
  9. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  10. accessSharedResourceWithSynchronized();
  11. });
  12. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  13. accessSharedResourceWithSynchronized();
  14. });

4.主线程和后台线程

iOS中有一个特别重要的线程概念是主线程(Main Thread):

主线程

用于更新UI和处理UI事件,以保证用户界面的流畅性。

Swift:

  1. DispatchQueue.main.async {
  2. // 更新UI
  3. // 例如,更新一个标签的文本:
  4. // self.label.text = "Updated Text"
  5. }

OC:

  1. dispatch_async(dispatch_get_main_queue(), ^{
  2. // 更新UI
  3. // 例如,更新一个标签的文本:
  4. // self.label.text = @"Updated Text";
  5. });

后台线程

用于执行耗时的任务,如网络请求或大量数据处理,避免阻塞主线程。

Swift:

  1. DispatchQueue.global(qos: .background).async {
  2. // 在这里执行耗时的任务
  3. // 例如,进行一个网络请求或大量数据处理
  4. let result = "Some Result"
  5. // 当需要更新UI时,回到主线程
  6. DispatchQueue.main.async {
  7. // 更新UI
  8. // self.label.text = result
  9. }
  10. }

OC:

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  2. // 在这里执行耗时的任务
  3. // 例如,进行一个网络请求或大量数据处理
  4. NSString *result = @"Some Result";
  5. // 当需要更新UI时,回到主线程
  6. dispatch_async(dispatch_get_main_queue(), ^{
  7. // 更新UI
  8. // self.label.text = result;
  9. });
  10. });

开发者应该始终记住在主线程上更新UI,并将耗时的工作放在后台线程上执行。例如,使用GCD可以很容易地在后台执行任务:

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  2. // 在后台线程上执行耗时任务
  3. // ...
  4. dispatch_async(dispatch_get_main_queue(), ^{
  5. // 回到主线程更新UI
  6. // ...
  7. });
  8. });

在iOS开发中,合理使用线程对于创建流畅、响应式的应用至关重要。避免在主线程上执行耗时操作,以及处理好线程间的同步和通信,是高效iOS应用程序的标志。

在 Swift 和 Objective-C 这两种 iOS 主要的编程语言中,线程使用的概念和操作都是相似的。

三、队列(Queue)

队列可以被视为对线程行为的一层抽象,但它们并不是线程本身。

队列提供了一种机制来组织和调度执行任务的顺序,而不需要开发者直接管理线程的创建、同步和其他复杂的线程生命周期问题。

当你操控队列时,你实际上是在告诉系统如何安排这些任务的执行。在iOS中,这通常是通过使用Grand Central Dispatch (GCD) 来实现的。GCD后台自动管理着一个线程池,并根据系统的负载动态地调整线程的使用。这意味着:

  • 当你向一个队列中添加任务时,你并不需要指定具体运行在哪个线程上,GCD会为你处理这些细节。
  • 如果任务被添加到串行队列,GCD会保证这些任务按照他们被添加的顺序一个接一个地运行。
  • 如果任务被添加到并发队列,GCD会同时运行多个任务,但具体并行的任务数目会由系统根据当前的负载情况来决定。
  • 对于主队列(主线程的队列),GCD会保证所有的任务都在主线程上执行,通常用于UI更新,以确保界面的流畅性。

由于队列抽象了线程的管理,所以开发者不需要(也不应该)去假设某个任务会在特定的线程上执行。相反,应该关注的是任务的组织、同步和他们的执行方式(串行或并发)。

总之,通过操作队列,你间接地管理了线程的行为,但你并没有直接操控线程。队列的抽象让你能够更专注于任务的逻辑本身,而不是底层的线程管理。这极大地简化了并发编程,并提高了代码的安全性和可维护性。

1.队列类型

(1).串行队列(Serial Queue)

  • 一次只执行一个任务。下一个任务必须等待当前任务完成后才能开始。
  • 任务按照添加到队列的顺序依次执行。
  • 保证任务执行的顺序性和排他性。

(2).并发队列(Concurrent Queue)

  • 可以同时执行多个任务。但是任务的开始仍然按照它们被添加到队列的顺序。
  • 任务可以并行执行,不保证任务的开始和结束顺序。
  • 适合执行互不依赖的任务。

(3).主队列(Main Queue)

主队列是一个特殊的串行队列,它用于在主线程上执行任务。由于它在主线程上执行任务,因此它通常用于更新 UI 或处理 UI 事件,以确保用户界面的更新是平滑且同步的。

获取主队列的方式如下:

Swift:

let mainQueue = DispatchQueue.main

使用 DispatchQueue.main,你可以将任务异步或同步地派发到主线程。通常,你会异步地将任务派发到主线程,以避免阻塞当前线程:

  1. DispatchQueue.main.async {
  2. // 更新UI等主线程任务
  3. }

请注意,尽管你可以同步地派发任务到主队列,但如果你在主线程上这样做,将会导致死锁,因为主线程会等待自己完成任务,这是一个逻辑错误。因此,通常你只会异步地将任务派发到主队列。

OC:

dispatch_queue_t mainQueue = dispatch_get_main_queue();

(4).全局并行队列(Global Concurrent Queues)

GCD 提供了几个不同优先级的全局并行队列,这些队列在应用程序是共享的。你可以使用它们来执行并发任务,而不需要自己创建并行队列。全局并行队列有四个优先级:高、默认、低和后台。

获取全局并行队列的方式如下:

Swift:

let globalQueue = DispatchQueue.global(qos: .default)

OC:

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

优先级可以是以下之一:

  • DISPATCH_QUEUE_PRIORITY_HIGH
  • DISPATCH_QUEUE_PRIORITY_DEFAULT
  • DISPATCH_QUEUE_PRIORITY_LOW
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND

(5).自定义队列(Custom Queues)

在iOS开发中,使用Grand Central Dispatch (GCD)可以创建自定义队列(Custom Queues)。自定义队列可以是串行的也可以是并行的,取决于你的需求。以下是创建自定义队列的基本步骤和示例:

创建串行队列

串行队列(Serial Queue)保证任务按照添加到队列中的顺序执行,一个接一个地执行。

Swift:

  1. // 创建自定义串行队列
  2. let serialQueue = DispatchQueue(label: "com.example.mySerialQueue")
  3. // 异步添加任务到队列
  4. serialQueue.async {
  5. // 这里添加需要执行的任务
  6. print("Task 1 started")
  7. // 假设这里有一些耗时操作
  8. print("Task 1 finished")
  9. }
  10. serialQueue.async {
  11. // 添加另一个任务
  12. print("Task 2 started")
  13. // 同样这里可能有耗时操作
  14. print("Task 2 finished")
  15. }
  16. // Task 1 和 Task 2 将按照添加的顺序执行

OC:

  1. // 创建自定义串行队列
  2. dispatch_queue_t serialQueue = dispatch_queue_create("com.example.mySerialQueue", DISPATCH_QUEUE_SERIAL);
  3. // 异步添加任务到队列
  4. dispatch_async(serialQueue, ^{
  5. NSLog(@"Task 1 started");
  6. // 假设这里有一些耗时操作
  7. NSLog(@"Task 1 finished");
  8. });
  9. dispatch_async(serialQueue, ^{
  10. NSLog(@"Task 2 started");
  11. // 同样这里可能有耗时操作
  12. NSLog(@"Task 2 finished");
  13. });
  14. // Task 1 和 Task 2 将按照添加的顺序执行
创建并行队列

并行队列(Concurrent Queue)允许多个任务并发执行。任务开始的顺序仍然符合其被添加到队列中的顺序,但是它们的执行和结束顺序可能是不一致的,因为任务可以在不同的线程上并发运行。

Swift:

  1. // 创建自定义并行队列
  2. let concurrentQueue = DispatchQueue(label: "com.example.myConcurrentQueue", attributes: .concurrent)
  3. // 异步添加任务到队列
  4. concurrentQueue.async {
  5. // 添加任务
  6. print("Task 1 started")
  7. // 假设耗时操作
  8. print("Task 1 finished")
  9. }
  10. concurrentQueue.async {
  11. // 添加另一个任务
  12. print("Task 2 started")
  13. // 假设耗时操作
  14. print("Task 2 finished")
  15. }
  16. // Task 1 和 Task 2 可能会同时执行

OC:

  1. // 创建自定义并行队列
  2. dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
  3. // 异步添加任务到队列
  4. dispatch_async(concurrentQueue, ^{
  5. NSLog(@"Task 1 started");
  6. // 假设耗时操作
  7. NSLog(@"Task 1 finished");
  8. });
  9. dispatch_async(concurrentQueue, ^{
  10. NSLog(@"Task 2 started");
  11. // 假设耗时操作
  12. NSLog(@"Task 2 finished");
  13. });
  14. // Task 1 和 Task 2 可能会同时执行
设置队列的优先级

自定义队列还可以设置服务质量(Quality of Service,QoS),以指示任务的重要性和执行优先级。

Swift:

  1. // 创建具有优先级的自定义串行队列
  2. let highPriorityQueue = DispatchQueue(label: "com.example.myHighPriorityQueue", qos: .userInitiated)
  3. // 用户初始化的任务通常比较紧急,但不会阻塞UI,比如加载操作
  4. highPriorityQueue.async {
  5. // 执行高优先级任务
  6. }

服务质量(QoS)选项包括:

  • .userInteractive: 需要立即结果的任务,用于更新UI、处理事件等。
  • .userInitiated: 用户期望立即得到结果的任务,比如滑动停止后的加载操作。
  • .default: 默认优先级,如果没有设置QoS,则队列会使用这个。
  • .utility: 长时间运行的任务,通常带有用户可见的进度指示,如下载文件。
  • .background: 用户不直接感知的任务,比如数据库维护、数据备份等。
  • .unspecified: 没有设置QoS。

通过设置不同的QoS,系统会为这些任务分配不同的资源,从而影响它们的执行优先级。

OC:

  1. // 创建具有优先级的自定义串行队列
  2. dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
  3. dispatch_queue_t highPriorityQueue = dispatch_queue_create("com.example.myHighPriorityQueue", qosAttribute);
  4. // 用户初始化的任务通常比较紧急,但不会阻塞UI,比如加载操作
  5. dispatch_async(highPriorityQueue, ^{
  6. // 执行高优先级任务
  7. NSLog(@"High priority task is being executed.");
  8. });

2.同步和异步执行

在GCD中,你可以选择同步(sync)或异步(async)地将任务添加到队列中:

同步 (sync)

在当前线程中执行任务,并阻塞该线程,直到任务完成。

Swift:

  1. // 获取一个队列(非主队列)
  2. let queue = DispatchQueue(label: "com.example.queue")
  3. // 同步执行任务
  4. queue.sync {
  5. // 这个任务会立即执行,并且调用线程会等待直到任务完成
  6. }
  7. // 这行代码只有在上面的同步任务完成后才会执行

注意:不要在主队列上同步提交任务,因为这会导致死锁。

OC:

  1. dispatch_sync(queue, ^{
  2. // 这个任务会立即执行,并且提交任务的线程会等待直到任务完成
  3. });
  4. // 这行代码只有在上面的任务完成后才会执行

使用同步执行时要特别注意,如果你在主队列(主线程的串行队列)上同步提交任务,将会导致死锁,因为主队列等待这个同步任务完成,而同步任务又在等待主队列(也就是它自己)空闲,从而形成了相互等待的局面。

异步 (async)

允许任务在另一线程中执行,不会阻塞当前线程,调用线程会等待任务完成。

Swift:

  1. // 获取一个队列
  2. let queue = DispatchQueue(label: "com.example.queue")
  3. // 异步执行任务
  4. queue.async {
  5. // 这个任务会被加入队列中,但不一定会立即执行
  6. }
  7. // 这行代码会在上面的异步任务提交后立即执行,而不会等待那个任务结束

OC:

  1. dispatch_async(queue, ^{
  2. // 这个任务会被加入队列中,但不会立即执行
  3. });
  4. // 这行代码会在上面的异步任务提交后立即执行,而不会等待那个任务结束

异步执行是并发程序设计中的一项基本技术,允许多个任务并行进行。在 GCD 中,异步提交到串行队列将使任务一个接一个地执行,而异步提交到并发队列则可以让多个任务同时进行。

3.队列的选择和使用

在iOS开发中,选择合适的队列和执行方式(同步或异步)对于保证应用性能和响应性至关重要。以下是一些基本指南:

  • 使用全局并发队列执行耗时的后台任务,如数据处理或网络请求。
  • 使用主队列更新UI或执行需要在主线程完成的任务,确保UI更新是安全的。
  • 使用自定义串行队列来执行需要顺序执行的任务,或者当你需要创建一个锁或者保护资源的访问时。
  • 使用自定义并发队列来自定义任务的并发执行,或者当你需要控制执行任务的优先级时。
  • 使用同步执行来等待当前任务完成,这通常在你需要立即得到任务结果时使用。
  • 使用异步执行来允许当前线程继续工作,不需要等待任务完成,这有助于避免阻塞UI或者响应用户交互。

四、任务(Task)

在iOS开发中,"任务"一词通常指的是需要完成的工作单元。任务可以是任何事情,从简单的计算到复杂的网络请求。iOS提供了几种不同的方式来处理任务,尤其是在多线程和并发编程方面。

以下是一些iOS开发中任务的基本概念和处理方式。

1.任务的类型

计算密集型任务

这些任务需要大量的CPU资源来进行计算。

例子:图像处理,大量数据计算。

I/O密集型任务

这些任务包括文件读写、网络请求等,它们通常等待系统资源的响应。

例子:从硬盘读取文件,从服务器下载数据。

UI更新任务

这些任务涉及更新用户界面,必须在主线程上执行。

例子:刷新表视图,更新进度条。

2.GCD中的任务

在Grand Central Dispatch (GCD)中,任务通常以两种形式提交给队列:

同步执行(Synchronous Execution)

  • dispatch_sync函数用于同步添加任务到指定的队列。
  • 调用线程会停止执行直到任务在队列中完成。
  • 这种方式可能会导致死锁,尤其是当在主队列上同步提交任务时。

异步执行(Asynchronous Execution)

  • dispatch_async函数用于异步添加任务到指定的队列。
  • 调用线程不会等待任务完成,可以继续执行其他工作。
  • 异步执行是实现并发的主要方式。

使用GCD

创建任务
  1. let queue = DispatchQueue(label: "com.example.myQueue", attributes: .concurrent)
  2. queue.async {
  3. // 执行耗时的任务
  4. let result = performCalculations()
  5. DispatchQueue.main.async {
  6. // 在主线程更新UI
  7. updateUI(with: result)
  8. }
  9. }
任务的取消和暂停

在GCD中,你不能直接取消已经在执行的任务,但是你可以通过在任务代码中添加取消检查点来协作地取消任务。

  1. let queue = DispatchQueue(label: "com.example.myQueue")
  2. var isCancelled = false
  3. queue.async {
  4. if isCancelled { return }
  5. // 执行任务
  6. }
  7. // 取消任务
  8. isCancelled = true

3.NSOperation中的任务

NSOperation是另一种用于执行任务的抽象,它比GCD提供更多的控制和灵活性。任务以NSOperation对象的形式表示,可以添加到NSOperationQueue中执行。

NSOperation

  • 是一个抽象类,需要使用其子类NSBlockOperation或自定义子类。
  • 可以设置完成块、添加依赖以及观察任务状态。

NSOperationQueue

  • 管理一组NSOperation对象的执行。
  • 可以控制并发操作的数量,以及启动、暂停、取消操作。

使用NSOperation

创建任务
  1. let queue = OperationQueue()
  2. let operation = BlockOperation {
  3. // 执行耗时的任务
  4. let result = performCalculations()
  5. OperationQueue.main.addOperation {
  6. // 在主线程更新UI
  7. updateUI(with: result)
  8. }
  9. }
  10. queue.addOperation(operation)
任务的取消和暂停

在NSOperation中,你可以使用cancel方法来请求取消一个操作,操作对象会检查isCancelled属性决定是否提前结束任务。

  1. let operation = BlockOperation {
  2. // 执行任务
  3. if operation.isCancelled {
  4. return
  5. }
  6. // 继续执行任务
  7. }
  8. operationQueue.addOperation(operation)
  9. // 取消操作
  10. operation.cancel()

4.任务的挑战

在处理任务时,开发者必须注意以下几个挑战:

线程安全

确保在多线程环境中共享资源的访问是安全的,例如使用锁或串行队列。

性能

合理利用多线程和并发,避免过多地创建线程导致的上下文切换开销。

死锁

避免因同步执行或资源竞争造成的死锁情况。

资源管理

合理分配和回收资源,如内存、文件句柄等。

用户体验

确保耗时操作不会阻塞主线程,影响UI的流畅度。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/403361
推荐阅读
相关标签
  

闽ICP备14008679号