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

news/2024/7/20 22:46:43 标签: 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:

import Foundation

// 线程执行的函数
func threadFunction(arg: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
    print("Thread is now running")
    // 执行一些任务
    print("Thread finishing")
    return nil
}

// 创建线程
var thread = pthread_t(bitPattern: 0)
var attribute = pthread_attr_t()

pthread_attr_init(&attribute)
pthread_create(&thread, &attribute, { arg in
    return threadFunction(arg: arg)
}, nil)
pthread_attr_destroy(&attribute)

// 等待线程结束
pthread_join(thread!, nil)

OC:

#import <Foundation/Foundation.h>
#import <pthread.h>

void *threadFunction(void *arg) {
    NSLog(@"Thread is now running");
    // 执行一些任务
    NSLog(@"Thread finishing");
    return NULL;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        pthread_t thread;
        pthread_attr_t attribute;
        
        pthread_attr_init(&attribute);
        pthread_create(&thread, &attribute, threadFunction, NULL);
        pthread_attr_destroy(&attribute);
        
        // 等待线程结束
        pthread_join(thread, NULL);
    }
    return 0;
}

(2).NSThread

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

Swift:

import Foundation

// 线程调用的方法
@objc class ThreadExample: NSObject {
    @objc func threadMethod() {
        print("Thread is now running")
        // 执行一些任务
        print("Thread finishing")
    }
}

// 创建并启动线程
let example = ThreadExample()
let thread = Thread(target: example, selector: #selector(ThreadExample.threadMethod), object: nil)
thread.start()

// 主线程继续执行其它任务
print("Main thread is not blocked")

OC:

#import <Foundation/Foundation.h>

@interface ThreadExample : NSObject
- (void)threadMethod;
@end

@implementation ThreadExample
- (void)threadMethod {
    @autoreleasepool {
        NSLog(@"Thread is now running");
        // 执行一些任务
        NSLog(@"Thread finishing");
    }
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ThreadExample *example = [[ThreadExample alloc] init];
        NSThread *thread = [[NSThread alloc] initWithTarget:example selector:@selector(threadMethod) object:nil];
        [thread start];
        
        // 主线程继续执行其它任务
        NSLog(@"Main thread is not blocked");
    }
    return 0;
}

(3).Grand Central Dispatch (GCD)

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

Swift:

import Foundation

// 创建串行队列
let serialQueue = DispatchQueue(label: "com.example.serialQueue")

// 创建并发队列
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

// 在串行队列上异步执行任务
serialQueue.async {
    for i in 1...3 {
        print("Serial Queue Async Task \(i)")
    }
}

// 在并发队列上异步执行任务
concurrentQueue.async {
    for i in 1...3 {
        print("Concurrent Queue Async Task \(i)")
    }
}

// 在主队列上延迟执行任务
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("This task is run on the main queue after 2 seconds")
}

// 使用全局并发队列
DispatchQueue.global().async {
    print("Global queue async task")
}

// 主线程继续执行其他任务
print("Main thread is not blocked")

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

override func viewDidLoad() {
        super.viewDidLoad()
        
        DispatchQueue.main.async {
            print("1")
        }
        print("2")
        
        // 创建串行队列
        let serialQueue = DispatchQueue(label: "com.example.serialQueue")
        // 在串行队列上异步执行任务
        serialQueue.async {
            print("3")
        }
        print("4")
         
        // 创建并发队列
        let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
        // 在并发队列上异步执行任务
        concurrentQueue.async {
            print("5")
        }
        print("6")
                
        DispatchQueue.global().sync {
            print("7")
        }
        print("8")

        // 获取一个后台队列(优先级为默认)
        let backgroundQueue = DispatchQueue.global(qos: .background)
        // 在后台队列上异步执行任务
        backgroundQueue.async {
            // 放置后台线程需要执行的任务
            print("9")
        }
        print("10")

        DispatchQueue.global().async {
            print("11")
        }
        print("12")
    }
  • 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:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建串行队列
        dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
        
        // 创建并发队列
        dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
        
        // 在串行队列上异步执行任务
        dispatch_async(serialQueue, ^{
            for (int i = 1; i <= 3; i++) {
                NSLog(@"Serial Queue Async Task %d", i);
            }
        });

        // 在并发队列上异步执行任务
        dispatch_async(concurrentQueue, ^{
            for (int i = 1; i <= 3; i++) {
                NSLog(@"Concurrent Queue Async Task %d", i);
            }
        });

        // 在主队列上延迟执行任务
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"This task is run on the main queue after 2 seconds");
        });

        // 使用全局并发队列
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"Global queue async task");
        });

        // 主线程继续执行其他任务
        NSLog(@"Main thread is not blocked");
    }
    return 0;
}

(4).Operation Queues

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

Swift:

import Foundation

// 创建一个操作队列
let operationQueue = OperationQueue()

// 创建一个NSBlockOperation来执行任务
let operation1 = BlockOperation {
    print("Operation 1 is being executed")
    // 模拟一些工作
    Thread.sleep(forTimeInterval: 2)
    print("Operation 1 has finished executing")
}

// 创建另一个操作
let operation2 = BlockOperation {
    print("Operation 2 is being executed")
    // 模拟一些工作
    Thread.sleep(forTimeInterval: 1)
    print("Operation 2 has finished executing")
}

// 添加操作依赖,operation2将会在operation1完成后才开始执行
operation2.addDependency(operation1)

// 将操作添加到队列中
operationQueue.addOperation(operation1)
operationQueue.addOperation(operation2)

// 添加一个操作到队列中,使用addOperation的闭包形式
operationQueue.addOperation {
    print("Operation 3 is being executed")
    // 执行一些工作
    print("Operation 3 has finished executing")
}

// 主线程继续执行其他任务
print("Main thread is not blocked")

OC:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建操作队列
        NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
        
        // 创建NSBlockOperation来执行任务
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"Operation 1 is being executed");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:2];
            NSLog(@"Operation 1 has finished executing");
        }];
        
        // 创建另一个操作
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"Operation 2 is being executed");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:1];
            NSLog(@"Operation 2 has finished executing");
        }];
        
        // 添加操作依赖,operation2将会在operation1完成后才开始执行
        [operation2 addDependency:operation1];
        
        // 将操作添加到队列中
        [operationQueue addOperation:operation1];
        [operationQueue addOperation:operation2];
        
        // 使用addOperationWithBlock添加另一个操作到队列中
        [operationQueue addOperationWithBlock:^{
            NSLog(@"Operation 3 is being executed");
            // 执行一些工作
            NSLog(@"Operation 3 has finished executing");
        }];
        
        // 主线程继续执行其他任务
        NSLog(@"Main thread is not blocked");
    }
    return 0;
}

3.线程的同步

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

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

(1).锁(Locks)

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

Swift:

import Foundation

var sharedResource = [String]()

let lock = NSLock()

func accessSharedResource() {
    lock.lock()
    // 安全地访问共享资源
    sharedResource.append("Data")
    lock.unlock()
}

// 在不同的线程中调用accessSharedResource()
DispatchQueue.global().async {
    accessSharedResource()
}

DispatchQueue.global().async {
    accessSharedResource()
}

OC:

#import <Foundation/Foundation.h>

NSMutableArray *sharedResource = [NSMutableArray array];

NSLock *lock = [[NSLock alloc] init];

void accessSharedResource() {
    [lock lock];
    // 安全地访问共享资源
    [sharedResource addObject:@"Data"];
    [lock unlock];
}

// 在不同的线程中调用accessSharedResource()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    accessSharedResource();
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    accessSharedResource();
});

(2).信号量(Semaphores)

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

Swift:

import Foundation

let semaphore = DispatchSemaphore(value: 1)

func accessSharedResourceWithSemaphore() {
    semaphore.wait() // 等待信号量
    // 安全地访问共享资源
    sharedResource.append("Data")
    semaphore.signal() // 释放信号量
}

// 在不同的线程中调用accessSharedResourceWithSemaphore()
DispatchQueue.global().async {
    accessSharedResourceWithSemaphore()
}

DispatchQueue.global().async {
    accessSharedResourceWithSemaphore()
}

OC:

#import <Foundation/Foundation.h>

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

void accessSharedResourceWithSemaphore() {
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量
    // 安全地访问共享资源
    [sharedResource addObject:@"Data"];
    dispatch_semaphore_signal(semaphore); // 释放信号量
}

// 在不同的线程中调用accessSharedResourceWithSemaphore()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    accessSharedResourceWithSemaphore();
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    accessSharedResourceWithSemaphore();
});

(3).原子操作

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

#import <libkern/OSAtomic.h>

__block volatile int32_t counter = 0;

void incrementCounter() {
    OSAtomicIncrement32(&counter);
}

// 在不同的线程中调用incrementCounter()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    incrementCounter();
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    incrementCounter();
});

(4).@synchronized

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

#import <Foundation/Foundation.h>

void accessSharedResourceWithSynchronized() {
    @synchronized(sharedResource) {
        // 安全地访问共享资源
        [sharedResource addObject:@"Data"];
    }
}

// 在不同的线程中调用accessSharedResourceWithSynchronized()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    accessSharedResourceWithSynchronized();
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    accessSharedResourceWithSynchronized();
});

4.主线程和后台线程

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

主线程

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

Swift:

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

OC:

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

后台线程

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

Swift:

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

OC:

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

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

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 在后台线程上执行耗时任务
    // ...

    dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程更新UI
        // ...
    });
});

在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,你可以将任务异步或同步地派发到主线程。通常,你会异步地将任务派发到主线程,以避免阻塞当前线程:

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

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

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:

// 创建自定义串行队列
let serialQueue = DispatchQueue(label: "com.example.mySerialQueue")

// 异步添加任务到队列
serialQueue.async {
    // 这里添加需要执行的任务
    print("Task 1 started")
    // 假设这里有一些耗时操作
    print("Task 1 finished")
}

serialQueue.async {
    // 添加另一个任务
    print("Task 2 started")
    // 同样这里可能有耗时操作
    print("Task 2 finished")
}

// Task 1 和 Task 2 将按照添加的顺序执行

OC:

// 创建自定义串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.mySerialQueue", DISPATCH_QUEUE_SERIAL);

// 异步添加任务到队列
dispatch_async(serialQueue, ^{
    NSLog(@"Task 1 started");
    // 假设这里有一些耗时操作
    NSLog(@"Task 1 finished");
});

dispatch_async(serialQueue, ^{
    NSLog(@"Task 2 started");
    // 同样这里可能有耗时操作
    NSLog(@"Task 2 finished");
});

// Task 1 和 Task 2 将按照添加的顺序执行
创建并行队列

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

Swift:

// 创建自定义并行队列
let concurrentQueue = DispatchQueue(label: "com.example.myConcurrentQueue", attributes: .concurrent)

// 异步添加任务到队列
concurrentQueue.async {
    // 添加任务
    print("Task 1 started")
    // 假设耗时操作
    print("Task 1 finished")
}

concurrentQueue.async {
    // 添加另一个任务
    print("Task 2 started")
    // 假设耗时操作
    print("Task 2 finished")
}

// Task 1 和 Task 2 可能会同时执行

OC:

// 创建自定义并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

// 异步添加任务到队列
dispatch_async(concurrentQueue, ^{
    NSLog(@"Task 1 started");
    // 假设耗时操作
    NSLog(@"Task 1 finished");
});

dispatch_async(concurrentQueue, ^{
    NSLog(@"Task 2 started");
    // 假设耗时操作
    NSLog(@"Task 2 finished");
});

// Task 1 和 Task 2 可能会同时执行
设置队列的优先级

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

Swift:

// 创建具有优先级的自定义串行队列
let highPriorityQueue = DispatchQueue(label: "com.example.myHighPriorityQueue", qos: .userInitiated)

// 用户初始化的任务通常比较紧急,但不会阻塞UI,比如加载操作
highPriorityQueue.async {
    // 执行高优先级任务
}

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

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

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

OC:

// 创建具有优先级的自定义串行队列
dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
dispatch_queue_t highPriorityQueue = dispatch_queue_create("com.example.myHighPriorityQueue", qosAttribute);

// 用户初始化的任务通常比较紧急,但不会阻塞UI,比如加载操作
dispatch_async(highPriorityQueue, ^{
    // 执行高优先级任务
    NSLog(@"High priority task is being executed.");
});

2.同步和异步执行

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

同步 (sync)

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

Swift:

// 获取一个队列(非主队列)
let queue = DispatchQueue(label: "com.example.queue")

// 同步执行任务
queue.sync {
    // 这个任务会立即执行,并且调用线程会等待直到任务完成
}

// 这行代码只有在上面的同步任务完成后才会执行

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

OC:

dispatch_sync(queue, ^{
    // 这个任务会立即执行,并且提交任务的线程会等待直到任务完成
});

// 这行代码只有在上面的任务完成后才会执行

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

异步 (async)

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

Swift:

// 获取一个队列
let queue = DispatchQueue(label: "com.example.queue")

// 异步执行任务
queue.async {
    // 这个任务会被加入队列中,但不一定会立即执行
}

// 这行代码会在上面的异步任务提交后立即执行,而不会等待那个任务结束

OC:

dispatch_async(queue, ^{
    // 这个任务会被加入队列中,但不会立即执行
});

// 这行代码会在上面的异步任务提交后立即执行,而不会等待那个任务结束

异步执行是并发程序设计中的一项基本技术,允许多个任务并行进行。在 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

创建任务
let queue = DispatchQueue(label: "com.example.myQueue", attributes: .concurrent)

queue.async {
    // 执行耗时的任务
    let result = performCalculations()

    DispatchQueue.main.async {
        // 在主线程更新UI
        updateUI(with: result)
    }
}
任务的取消和暂停

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

let queue = DispatchQueue(label: "com.example.myQueue")
var isCancelled = false

queue.async {
    if isCancelled { return }
    // 执行任务
}

// 取消任务
isCancelled = true

3.NSOperation中的任务

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

NSOperation

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

NSOperationQueue

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

使用NSOperation

创建任务
let queue = OperationQueue()

let operation = BlockOperation {
    // 执行耗时的任务
    let result = performCalculations()
    
    OperationQueue.main.addOperation {
        // 在主线程更新UI
        updateUI(with: result)
    }
}

queue.addOperation(operation)
任务的取消和暂停

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

let operation = BlockOperation {
    // 执行任务
    if operation.isCancelled {
        return
    }
    // 继续执行任务
}

operationQueue.addOperation(operation)

// 取消操作
operation.cancel()

4.任务的挑战

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

线程安全

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

性能

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

死锁

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

资源管理

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

用户体验

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


http://www.niftyadmin.cn/n/5481361.html

相关文章

Qt5中使用QPrinter和QprintDialog类

学习Qt过程中&#xff0c;做一个简单的编辑器&#xff0c;其中需要使用到打印文本功能&#xff0c;在使用Qt printer时遇到了几个麻烦。 一、在使用到QPrinter和QprintDialog类时的附加处理 ①若是在qt creator中&#xff0c;需要在 &#xff08;.pro&#xff09;工程文件中加…

【免安装的MATLAB--MATLAB online】

目录&#xff1a; 前言账号的注册图片处理的示例脚本函数 总结 前言 在计算机、数学等相关专业中&#xff0c;或多或少都会与MATLAB产生藕断丝连的联系&#xff0c;如果你需要使用MATLAB&#xff0c;但是又不想要安装到自己的电脑上&#xff08;它实在是太大了啊&#xff01;动…

基于STM32的智能家居系统设计与实现:手机APP与语音控制家电

基于STM32的智能家居系统设计与实现&#xff1a;手机APP与语音控制家电 一、研究背景与意义 随着科技的进步和人们生活水平的提高&#xff0c;智能家居系统逐渐成为现代家庭的新宠。智能家居系统通过集成各种智能设备和传感器&#xff0c;为用户提供更加便捷、舒适和安全的家…

前端三剑客 —— JavaScript (第七节)

内容回顾 DOM编程 document对象 有属性 有方法 节点类型 元素节点 属性节点 文本节点 操作DOM属性 DOM对象.属性名称 DOM对象[属性名称] 调用DOM对象的API 操作DOM样式 获取有单位的样式值 标签对象.style.样式名称&#xff0c;这种方式只能操作行内样式。 使用getComputedSty…

web蓝桥杯真题:新鲜的蔬菜

代码&#xff1a; .box {display: flex; } #box1 {align-items: center;justify-content: center; }#box2 {justify-content: space-between; } #box2 .item:nth-child(2) {align-self: end; }#box3 {justify-content: space-between; } #box3 .item:nth-child(2) {align-self…

靶机渗透lazysysteemadmin

前期工作 查看本机ip 查询靶机ip 靶机ip&#xff1a;192.168.87.141 扫下端口 看下网站 先无脑使用wpscan搜索漏洞 wpscan -url XX(ip)/wordpress wpscan教程&#xff1a;【网安神器篇】——WPScan漏洞扫描工具-CSDN博客 ┌──(root&#x1f480;kali)-[~] └─# wpscan…

IDEA中的Debug功能介绍

说明&#xff1a;本文介绍IDEA中的Debug功能&#xff0c;基于2023.2&#xff08;Ultimate Edition&#xff09;版本 简单介绍 首先&#xff0c;在程序需要停止的所在行号上&#xff0c;鼠标左键&#xff0c;可设置一个断点&#xff0c;是一个红色圆点标志&#xff0c;表示程序…

HarmonyOS 开发-阻塞事件冒泡

介绍 本示例主要介绍在点击事件中&#xff0c;子组件enabled属性设置为false的时候&#xff0c;如何解决点击子组件模块区域会触发父组件的点击事件问题&#xff1b;以及触摸事件中当子组件触发触摸事件的时候&#xff0c;父组件如果设置触摸事件的话&#xff0c;如何解决父组…