当前位置: 首页 > article >正文

iOS主要知识点梳理回顾-2-多线程

iOS的多线程主要有三种方式,NSThread、GCD(Grand Central Dispatch)NSOperationQueue

        开始,在iOS2发布的时候,苹果同步推出了NSthread和NSOperation。其中NSthread比较简单,仅提供了创建队列、开始、取消、关闭等简单动作。而NSOperation就更高级一些,他是基于队列的任务管理工具,有任务关系、优先级、并发限制、任务取消等功能,相对于NSthread他封装的更好一些,也重一些。这2种方式基本满足了当时开发者的需求,简单开异步任务就用NSThread,任务之间存在关系就用NSOperation + NSOperationQueue来实现。

        2年后,出于降低能耗及更好的适配苹果的硬件等目的,随着iOS4的发布,苹果推出了GCD。GCD的能力基本和NSOperation类似,主要差异是他更轻量、灵活。GCD 提供了一个底层的并发框架,其中包含了线程池和队列调度等机制,用于更有效地管理并发任务。推出GCD的同时,苹果也顺带把NSOperationQueue的内部实现做了优化,应用了GCD的一些(或大部分)研发成果。所以可以根据个人习惯选择多线程工具。

NSThread

- (void)createAndStartThread {
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runTask) object:nil];
    [thread start];
}

- (void)runTask {
    
    NSLog(@"线程开始: %@", [NSThread currentThread]);
    
    // 模拟耗时任务
    [NSThread sleepForTimeInterval:1.0]; 
    
    // 退出
    [NSThread exit];

    //
    NSLog(@"这行不会执行");
}

NSOperation

最基本就不写了,感觉和调用方法没啥区别

依赖关系
- (void)operationWithDependencies {
    NSLog(@"create operationWithDependencies on %@", NSThread.currentThread);
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务 1 执行 %@", NSThread.currentThread);
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务 2 执行 %@", NSThread.currentThread);
    }];
    
    // operation1 等待 operation2 完成
    [operation1 addDependency:operation2];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation1, operation2] waitUntilFinished:NO];
    
    NSLog(@"operationWithDependencies cimmit");
}

打印结果

create operationWithDependencies on <_NSMainThread: 0x60000170c000>{number = 1, name = main}

operationWithDependencies cimmit

任务 2 执行 <NSThread: 0x600001752500>{number = 4, name = (null)}

任务 1 执行 <NSThread: 0x600001763200>{number = 6, name = (null)}

如果我们设置了 waitUntilFinished YES,则相同于同步队列,程序会最后打印“operationWithDependencies cimmit

其他功能
  1. 设置最大并发数
  2. 取消所有队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 设置最多并发 2 个任务
queue.maxConcurrentOperationCount = 2;

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务1执行 %@", NSThread.currentThread);
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务2执行 %@", NSThread.currentThread);
}];
    
[queue addOperations:@[operation1, operation2] waitUntilFinished:YES];

// 取消所有队列
[queue cancelAllOperations];
    

GCD(Grand Central Dispatch)

并发队列

异步任务在非主线程中执行

    dispatch_queue_t queue = dispatch_queue_create("com.example.myQueue", DISPATCH_QUEUE_CONCURRENT);
    
    // 同步任务(阻塞当前线程)
    dispatch_sync(queue, ^{
        NSLog(@"同步任务 - %@", [NSThread currentThread]);
    });
    
    // 异步任务(不阻塞当前线程)
    dispatch_async(queue, ^{
        NSLog(@"异步任务 - %@", [NSThread currentThread]);
    });
全局并发

各自线程不一致,无序

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"全局并发队列任务1 - %@", [NSThread currentThread]);
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"全局并发队列任务2 - %@", [NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"全局并发队列任务3 - %@", [NSThread currentThread]);
    });
串行队列

以下代码推荐小白朋友反复执行观察规律。串行队列有个特点,加入队列的任务会严格按着加入顺序执行,无论他是同步还是异步。当同步任务被添加到队列后,程序会等待同步任务前加入的所有任务执行完毕,然后执行同步任务,然后继续往后执行。其中同步任务在主线程执行,异步任务在同一个子线程执行。

    dispatch_queue_t queue = dispatch_queue_create("com.example.barrier", nil);

    dispatch_async(queue, ^{
        NSLog(@"异步任务1 %@", NSThread.currentThread);
    });

    dispatch_async(queue, ^{
        NSLog(@"异步任务2 %@", NSThread.currentThread);
    });

    dispatch_sync(queue, ^{
        NSLog(@"同步任务 %@", NSThread.currentThread);
    });

    dispatch_async(queue, ^{
        NSLog(@"异步任务3 %@", NSThread.currentThread);
    });
    
    NSLog(@"主线程任务");

以上代码,4个任务的顺序是绝对不变的,主线程任务一定会在同步任务后执行,可能在3前,可能在3后,这事不确定的。

任务组 dispatch_group
   dispatch_group_t group = dispatch_group_create();

    // 自动入离队1
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务1");
    });

    // 自动入离队2
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务2");
    });
    
    // 手动入队
    dispatch_group_enter(group);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延时任务完成");
        // 手动离队
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"所有任务完成,回到主线程");
    });
栅栏任务
dispatch_queue_t queue = dispatch_queue_create("com.example.barrier", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"任务1");
});

dispatch_async(queue, ^{
    NSLog(@"任务2");
});

dispatch_barrier_async(queue, ^{
    NSLog(@"栅栏任务");
});

dispatch_async(queue, ^{
    NSLog(@"任务3");
});
信号量

可以用于控制并发数,实现锁的效果等

- (void)concurrentTaskControl {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); // 最大并发数为2
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    for (int i = 0; i < 5; i++) {
        dispatch_async(queue, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 请求信号量
            NSLog(@"任务 %d 开始 - %@", i, [NSThread currentThread]);
            sleep(2);  // 模拟耗时任务
            NSLog(@"任务 %d 完成", i);
            dispatch_semaphore_signal(semaphore); // 释放信号量
        });
    }
}
其他场景

1、延迟执行dispatch_after,我经常用它解决一些定期权限检查之类的,就可以不用定时器

2、单例dispatch_once_t

3、并行遍历dispatch_apply

一些细节对比

1)任务组:DispatchGroup和NSOperationQueue类似,区别在于DispatchGroup提供的函数notify(queue:execute:)、enter()、leave()等,使得任务和队列之间的关系更灵活,不必强依赖于任务的状态。而OperationQueue中任务的整体管理依赖任务的状态,OperationQueue取消之后,如果任务还未开始执行,将不再执行;如果任务正在执行中,可继续执行,特殊情况需要终断的,可通过Operation的isCancelled属性来判断是否被取消了。

2)GCD的通过信号量来控制某个队列并发数,而OperationQueue用的是maxConcurrentOperationCount属性来控制,用起来差异不大,OperationQueue的代码更少、更直观。

3)GCD没有任务依赖关系的直接设置,但是可以通过栅栏(dispatchBarriers)来实现先后任务等待,NSOperation可以直接设置谁依赖谁,更直接。

4)GCD和NSOperation都能设置任务优先级,但是不要以为高优先级的任务就一定比低优先级的任务先执行,具体的执行时间还受到其他因素的影响,比如操作的依赖关系、操作的并发性设置等。


http://www.kler.cn/a/539806.html

相关文章:

  • Centos挂载镜像制作本地yum源,并补装图形界面
  • 十二、Docker Compose 部署 SpringCloudAlibaba 微服务
  • deepseek+kimi自动生成ppt
  • 51单片机之引脚图(详解)
  • 【Matlab优化算法-第14期】基于智能优化算法的VMD信号去噪项目实践
  • 移植BOA服务器到GEC2440开发板
  • docker常用命令及案例
  • 【R语言】相关系数
  • Ubuntu禁止内核自动更新
  • 【Java八股】JVM
  • 为什么推荐使用 LabVIEW 开发
  • 日志2025.2.9
  • Java面试题整理一(反射)
  • c++初始
  • Ext系列文件系统(上)
  • C++ Primer 逗号运算符
  • Linux中getifaddrs函数
  • 【人工智能】解码语言之谜:使用Python构建神经机器翻译系统
  • 51单片机之冯·诺依曼结构
  • 爬虫学习笔记之requests库的使用
  • 数据可视化技术综述(4)衡量数据的性能指标的十大维度
  • [Deepseek+Heygen+剪映]快速生产数字人讲解的视频内容
  • 【机器学习】scikit-learn调用KNN算法并手动模仿封装自己的KNN算法
  • 深入解析 FFmpeg 的 AAC 编解码过程
  • Python 鼠标轨迹 - 防止游戏检测
  • NPM 的扁平化目录与幻影依赖问题,以及 PNPM 如何通过硬链接和软链接解决