首页 > 其他分享 >多线程

多线程

时间:2024-01-02 21:31:59浏览次数:29  
标签:多线程 队列 dispatch queue 任务 线程 执行

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

NSOperationNSOperationQueue 是一套强大的 API,是对GCD的更高一层的封装。

NSOperation

NSOperation 是一个抽象类,它封装了要在后台执行的单个任务。你不能直接使用 NSOperation,而是应该使用它的两个具体子类 NSBlockOperationNSInvocationOperation,或者创建自定义的 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


标签:多线程,队列,dispatch,queue,任务,线程,执行
From: https://blog.51cto.com/u_16456705/9073090

相关文章

  • Bmwgroupdesignworks爬虫,网站作品信息多线程采集爬虫源码!
    一个比较简单国外设计站点,作品信息采集爬虫源码,比较简单,采集的内容包括标题、内容及图片信息,适合新人学习参考使用。网站作品信息采集中,关于图片的采集下载使用了重试以及多线程的方式爬取采集下载,适合Python爬虫新人练手使用和学习,如果你正在找练手网站,不妨尝试爬取下载数据。三......
  • 多线程循环打印123
    1、多线程循环打印123importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassPrintThread{privateLocklock=newReentrantLock();privatevolatileintflag......
  • 关于python3多线程和协程
    以下内容部分由chatgpt生成,本文仅作为备忘和记录。asyncio.sleep和time.sleep都是用于在Python中进行延迟操作的函数,但它们的工作方式和使用场景有一些不同。asyncio.sleep:asyncio.sleep是用于在异步代码中进行暂停的函数,它是asyncio模块中的一部分。在异步程序中......
  • nodejs多线程-共享内容
    前言:昨天遇到基于Nodejs启动多线程,以便不同服务之间可以调用(共享内存) worker_threadsnode官方文档注明了:worker_threads模块允许使用并行地执行JavaScript的线程。与child_process或cluster不同,worker_threads可以共享内存。它们通过传输ArrayBuffer实例或共享Sh......
  • java-多线程编程
    多线程是指在一个程序中同时执行多个线程,每个线程都是独立运行的。Java中的多线程编程允许在同一个程序中同时执行多个任务,以提高程序的效率和响应性。以下是一些与Java多线程编程相关的重要概念:线程(Thread):线程是程序的执行单元,可以并发执行多个任务。在Java中,可以通过创建Thre......
  • Java多线程:数据一致性问题及解决方案
    引言在面向对象的编程语言Java中,多线程编程是一个强大的工具,可以使我们能够构建高效率和高并发的应用程序。然而,多线程环境下的数据共享也带来了数据一致性的挑战。在本文中,我们将探讨Java多线程中的数据一致性问题,并提出几种解决方案。数据一致性问题当多个线程同时对共享资源进行......
  • 深入探究多线程中的虚假唤醒现象--从生产者消费者问题到高级解决方案的全方位解读
    文章目录生产者和消费者问题虚假呼唤问题解决方案线程之间的虚假唤醒问题常出现在多线程编程中。我看国内很多教程都解释的稀里糊涂的,所以打算写一篇博客好好絮叨絮叨。首先看一下线程虚假唤醒的定义:多线程环境下,有多个线程执行了wait()方法,需要其他线程执行notify()或者notifyAl......
  • 死锁、内部碎片、外部碎片、多进程/多线程、服务器高并发、集群
    死锁:两个(多个)线程互相等待对方数据的过程1.死锁产生条件(解决办法):......
  • Qt 中的多线程 02:移动工作对象到子线程中
    Qt中的多线程除了通过继承QThread类,然后重写虚函数run()之外还有另一种方案:将要执行的复杂操作,放在一个继承自QObject的类中,然后使用QObject::moveToThread(QThread*)函数将工作对象的移动到创建的子线程中去执行。工作对象类该类继承自QObject主要是用来实现一些耗......
  • Java多线程:状态转移详解
    Java中的线程在其生命周期内会经历不同的状态。理解这些状态以及它们之间的转换对于掌握多线程编程至关重要。本文将详细介绍Java线程的状态以及它们之间的转换机制。线程生命周期状态根据Java语言规范和java.lang.Thread.State枚举定义,线程的状态主要分为以下几种:新建(NEW):创建后尚......