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

objectivec 复制代码
- (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

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

依赖关系
objectivec 复制代码
- (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. 取消所有队列
objectivec 复制代码
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)

并发队列

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

objectivec 复制代码
    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]);
    });
全局并发

各自线程不一致,无序

objectivec 复制代码
    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]);
    });
串行队列

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

objectivec 复制代码
    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
objectivec 复制代码
   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(@"所有任务完成,回到主线程");
    });
栅栏任务
objectivec 复制代码
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");
});
信号量

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

objectivec 复制代码
- (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都能设置任务优先级,但是不要以为高优先级的任务就一定比低优先级的任务先执行,具体的执行时间还受到其他因素的影响,比如操作的依赖关系、操作的并发性设置等。

相关推荐
similar71833 分钟前
iOS pod install一直失败,访问github超时记录
ios·github
没头脑的ht8 小时前
UITableView的复用原理
ios·swift
又菜又爱coding16 小时前
iOS AES/CBC/CTR加解密以及AES-CMAC
ios
浏览器爱好者1 天前
如何使用Xcode进行iOS应用开发?
macos·ios·xcode
IT规划师2 天前
并发编程 - 线程同步(六)之锁lock
多线程·并发编程·线程同步
胖虎12 天前
iOS 自动翻滚广告条(榜单条)实现方案
ios·广告滚动·纵向轮播
KWMax2 天前
DeepSeek深度思考:客户端(Android/iOS)架构设计指南
android·ios·架构设计·deepseek
haomo20142 天前
苹果可折叠iPad:2028年的科技盛宴?
前端·科技·ios·ipad
ii_best2 天前
安卓/ios脚本开发按键精灵经验小分享
android·ios·个人开发