多线程以及底层实现
Posted LoSenTrad
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程以及底层实现相关的知识,希望对你有一定的参考价值。
一.概念
- 什么是进程
- 进程是指在系统中正在运行的一个应用程序
- 每个进程之间的是独立的,每个进程均运行在其专用且受保护的内存空间内
- 一个进程至少要有一个线程
- 什么是线程
- 一个线程要执行任务,必须得有线程
- 一个进程(程序)的所有任务都在线程中执行的
- 一个线程执行任务是串行的,也就是说一个线程,同一时间内,只能执行一个任务
- 多线程原理
- 同一时间,CPU只能处理1条线程,只有一条线程在工作(执行)
- 多线程并发(同时)执行,其实质是CPU快速的在多线程之间调度(切换)
- 如果线程过多,会怎样?
- CPU在N多条线程中调度,会消耗大量的cpu资源
- 每条线程被调度执行的频率越低(线程的执行效率低)
- 多线程的优点
- 能适当提高程序的执行效率
- 能适当提高资源的利用率(CPU 内存利用率等)
- 多线程的缺点
- 创建线程是有开销的,ios下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间
- 如果开启大量的线程,会降低程序的性能
- 程序越多CPU的线程上的开销就越大
- 程序设计更加复杂:线程之间的通讯,多线程的数据共享
- 什么是主线程
- 一个ios程序运行后,会开启一条线程,称为主线程,或者是UI线程
- 主线程的主要作用
- 显示和刷新UI界面
- 处理UI事件(比如点击事件,滚动事件,拖拽事件等)
- 主线程的使用注意
- 别将比较耗时的操作放在主线程中,会导致UI界面的卡顿
- 将耗时操作放在子线程(后台线程,非主线程)
二.多线程的4种方案
1.C语言的POSIX接口:#include
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程```
- 其他用法
```objc//创建NSThread对象
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:nil object:nil];
//获取当前线程
NSThread *thread1 = [NSThread currentThread];
//设置线程的名字
thread.name = @"这是设置线程名字的";
[thread setName:@"这也是设置线程的名字的"];```
- 线程状态
![这里写图片描述](https://img-blog.csdn.net/20160615133614638)
- 互斥锁
- @synchronized(锁对象) { // 需要锁定的代码 }
注意:锁定1份代码只用1把锁(同个对象),用多把锁是无效的
- 优点:能有效防止因多线程抢夺资源造成的额数据安全问题
- 缺点:需要消耗大量的CPU资源
- 使用前提:多线程同一时间抢夺同一块资源
- 互斥锁就是使用了线程同步技术.就是多线程在按顺序执行
- OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁
###1.常用的方法
```objc
//在主线程中执行任务
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
//YES表示等Selector执行完后才执行后面
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//开辟一条子线程执行任务
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array
//开辟一条子线程执行任务
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait
//在后台执行任务(相当于开辟了一个子线程)
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg
//类方法,开辟一条子线程执行任务
[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"hello"];
<div class="se-preview-section-delimiter"></div>
//开启子线程,并且要通过手动开启线程,否则无法执行任务的
// 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"Alloc"];
// 开启线程,将线程添加到线程可调度池里,等待CPU的调度
[thread start];
<div class="se-preview-section-delimiter"></div>
//线程睡眠
//睡眠1秒
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
//线程睡眠1秒
[NSThread sleepForTimeInterval:1.0];
<div class="se-preview-section-delimiter"></div>
//线程退出,千万不要在主线程退出,否则程序就会出现各种问题,包括奔溃,黑屏,不能响应
[NSThread exit];
<div class="se-preview-section-delimiter"></div>
2.线程的取消
// 创建线程--> 新建状态
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
// 开启线程--> 将线程添加到可调度线程池中,等待CPU调度
[thread start];
// 睡
[NSThread sleepForTimeInterval:0.2];
// 取消线程执行,取消线程仅仅是给线程打上一个被取消的标记
// 要实现真正取消线程的执行需要在线程内部的关键节点进行判断
[thread cancel];
NSLog(@"over");
<div class="se-preview-section-delimiter"></div>
3.线程的优先级
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:@"---"];
thread.name = @"线程A";
//设置线程的优先级,范围是0到1
thread.threadPriority = 1.0;
[thread start];
<div class="se-preview-section-delimiter"></div>
4.线程栈区的大小
// 不管主线程还是子线程,栈区大小默认都是512kb.
// NSLog(@"stackSize = %zd",[NSThread currentThread].stackSize / 1024);
// 最小的栈区大小是16kb,必须是4的整数倍,建议不要修改栈区大小,使用默认即可.
[NSThread currentThread].stackSize = 1024 * 1024;
NSLog(
@"stackSize = %zd",[NSThread currentThread].stackSize / 1024)
<div class="se-preview-section-delimiter"></div>
5.多线程访问数据导致的数据异常的处理
多个线程访问数据的时候会导致数据异常,这时候我们要使用互斥锁
// 先保证单条线程能正确工作:能够买完所有票
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.ticket = 20;
// 创建线程
NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
threadA.name = @"美女A";
// 开启线程
[threadA start];
// 创建线程
NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
threadB.name = @"美女B";
// 开启线程
[threadB start];
}
- (void)saleTicket {
while (YES)
{
// 睡
[NSThread sleepForTimeInterval:1.0];
// 使用互斥锁:可以保证锁住的代码同一时间只有一个线程访问.
// 使用互斥锁锁住的代码要尽可能少,锁住关键节点的代码即可.
// [[NSUserDefaults standardUserDefaults] synchronize];
//@synchronized
// self:锁对象,可以是任意NSObject类的对象.
// 注意点:锁对象必须是所有线程对象能同时访问的对象.
// 如果只有一个地方使用到互斥锁,一般锁对象就是self`,避免再创建一个对象.
// NSObject *lockObj = [[NSObject alloc] init];
@synchronized(self)
{
// 判断是否有剩余的票数
if(self.ticket > 0)
{
// 如果有,则卖一张
self.ticket --;
NSLog(@"%@卖了一张票,剩余的票数:%zd",[NSThread currentThread].name, self.ticket);
continue;
}
}
// 没有票,则提示用户票没了
NSLog(@"票没了");
break;
}
NSLog(@"over");
}
<div class="se-preview-section-delimiter"></div>
拓展:
•nonatomic 与 atomic
1)iOS中还有一种锁 原子锁atomic
2)nonatomic 非原子属性 (线程不安全),
3)atomic 原子属性,默认都是"原子"属性 (线程安全),也不能保证数据写入的正确性 4)原子属性,也是一个多线程技术,setter/getter函数是一个原子操作,如果多线程同时调用setter
时,不会出现某一个线程执行完setter所有语句之前,另一个线程就开始执行setter,相当于函数头尾 加了锁. 这样的话并发访问性能会比较低.
提示:设置原子属性后,不要自己去写原子属性的setter方法
原因:原子属性默认的setter方法中,使用了“128位自旋锁”,性能比互斥锁高,但是同样消耗性 能
<div class="se-preview-section-delimiter"></div>
- ##3.C语言的GCD(性能好,代码更精简)
- 什么是GCD
- 全称是 Grand Central Dispatch,可译为“牛逼的中枢调度器”
- 纯C语言,提供了非常多强大的函数
- GCD的优势
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
- GCD的有四种队列:串行队列,并行队列,全局队列,主队列.
- GCD有两种操作:异步操作,同步操作.
- 异步操作dispatch_async,”会并发执行”,无法确定任务的执行顺序
- 同步操作dispatch_sync,”会依次顺序执行”,能够决定任务的执行顺序
- 主队列可以看成串行队列,如果在主线程中使用同步操作,那就会造成线程阻塞
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程,线程是操作
系统可识别的最小执行和调度单位.
1.CGD的常用使用
注意:在主线程中,执行同步操作,里面的任务将不会执行(阻塞),同步异步决定是否可以开子线程,队列决定任务是怎么执行的.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [self gcdDemo1];//cgd的同步和异步执行任务.
// [self gcdDemo2];
// [self gcdDemo3];
}
//cgd的同步和异步执行任务.
-(void)gcdDemo1
{
void (^gcdBlock)() = ^()
{
NSLog(@"这是简单的block,%@",[NSThread currentThread]);
};
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, gcdBlock);//开启子线程
dispatch_async(queue, gcdBlock);//不开启子线程
}
//gcd的常用写法(精简版)
- (void)gcdDemo2
{
//在全局队列异步执行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"async---%@",[NSThread currentThread]);
});
//在主队列中异步执行.(没有开启子线程)
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"async---%@",[NSThread currentThread]);
});
//在主队列同步执行
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync---%@",[NSThread currentThread]);
});
}
//线程间的通讯
- (void)gcdDemo3
{
//在异步开启子线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"这是耗时任务---%@",[NSThread currentThread]);
//异步在主线程执行任务
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程刷新UI--%@",[NSThread currentThread]);
});
});
}
<div class="se-preview-section-delimiter"></div>
2.CGD中的串行队列
/// 串行队列异步执行
/// 提问:会不会开线程? 是否是顺序执行?
/// 问答:会开线程 开多条 是
- (void)gcdDemo3{
for (int i = 0; i < 10; i++) {
// 参数1:队列的名称
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
// 将任务添加到队列中,并指定执行任务的函数
dispatch_async(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
/// 串行队列同步执行
/// 提问:会不会开线程? 是否是顺序执行?
/// 问答:不会开线程 是
- (void)gcdDemo1 {
// 创建串行队列
// 参数1:队列的名称
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; ++i) {
// 将任务添加到队列中,并指定执行任务的函数
dispatch_sync(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
/// 串行队列异步执行
/// 提问:会不会开线程? 开几条线程? 是否是顺序执行?
/// 问答:会开线程 开1条 是 全对
- (void)gcdDemo2 {
// 创建串行队列
// 参数1:队列的名称
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; ++i) {
// 将任务添加到队列中,并指定执行任务的函数
dispatch_async(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
<div class="se-preview-section-delimiter"></div>
3.GCD中的并发队列
/// 并发队列同步执行
/// 提问:会不会开线程? 开几条? 是否是顺序执行?
/// 答案:不会开 顺序
- (void)gcdDemo1 {
// 创建串行队列
// 参数1:队列的名称
for (int i = 0; i < 10; ++i) {
// 将任务添加到队列中,并指定执行任务的函数
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
}
/// 串行队列异步执行
/// 提问:会不会开线程? 开几条线程? 是否是顺序执行?
/// 问答:会开线程 不知道,由底层线程池决定 不是
- (void)gcdDemo2 {
// 创建串行队列
// 参数1:队列的名称
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 5; ++i) {
// 将任务添加到队列中,并指定执行任务的函数
dispatch_async(queue, ^ {
NSLog(@"%@--%d",[NSThread currentThread],i);
});
}
NSLog(@"下载xxxx=%@",[NSThread currentThread]);
NSLog(@"下载B=%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"下载C=%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"下载A=%@",[NSThread currentThread]);
});
}
<div class="se-preview-section-delimiter"></div>
4.GCD中的主队列(特殊的串行队列)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"start");
[self gcdDemo3];
NSLog(@"end");
}
///主队列同步执行不死锁
- (void)gcdDemo3 {
// 获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"over = %@",[NSThread currentThread]);
// 同步任务
dispatch_sync(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],100);
});
});
}
///主队列同步执行
- (void)gcdDemo2 {
// 获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 同步任务
// 主队列中的任务必须在主线程空闲的时才会执行.
// 主队列中添加同步任务会造成死锁.
dispatch_sync(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],100);
});
NSLog(@"over");
}
//主队列异步执行
- (void)gcdDemo1 {
// 获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i < 5; ++i) {
// 异步任务
dispatch_async(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],i);
});
}
}
<div class="se-preview-section-delimiter"></div>
5.GCD中的全局队列
//全局队列异步执行
//全局队列特点跟并发队列是一样的
// dispatch_get_global_queue:获得全局队列不需要关心什么时候销毁
// 自己创建的并发队列需要在不使用的时候销毁
// 一般开发第三方框架时会是用自定义并发队列.
- (void)gcdDemo1 {
// 获得主队列
// Flags that are reserved for future use. Always specify 0 for this parameter
// 参数1:
// 参数2:保留给未来使用,永远给个0
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; ++i) {
// 异步任务
dispatch_async(queue, ^{
NSLog(@"%@---%zd",[NSThread currentThread],i);
});
}
// 在ARC中不允许调用release方法
// dispatch_release(queue);
}
<div class="se-preview-section-delimiter"></div>
6.CGD中的dispatch_barrier_async,barrier异步的使用
使用 dispatch_barrier_async 添加的 block 会在之前添加的 block 全部运行结束之后,统一在同一个线程顺序执行,从而保证对非线程安全的对象进行正确的操作!
- (void)viewDidLoad {
[super viewDidLoad];
// 创建并发队列
queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
// dispatch_barrier_async:一定是要使用自定义并发队列
// queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 20; ++i) {
[self loadImage:i];
}
}
/// 加载index索引指定图片
// 主要用于在多个异步操作完成之后,统一对非线程安全的对象进行更新
- (void)loadImage:(NSInteger)index{
dispatch_async(queue, ^{
// 获得图片名称
NSString *imageName = [NSString stringWithFormat:@"%zd.jpg",index % 9];
// 获得图片路径
NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:nil];
// 加载图片
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
NSLog(@"下载第%zd张图片,%@",index,[NSThread currentThread]);
dispatch_barrier_async(queue, ^{
NSLog(@"第%ld张图片下载完成=%@,",(long)index,[NSThread currentThread]);
// 将图片添加到数组中
// NSMutableArray不是线程安全的类
// NSMutableDictionary也不是线程安全的类的
// 凡是带有mutable单词的类都不是线程安全.
[self.images addObject:image];
});
});
}
<div class="se-preview-section-delimiter"></div>
7.dispatch_after操作和dispatch_once
1.dispatch_after
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC));
//延时操作,这里是开启了子线程的
dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
NSLog(@"延迟操作是子线程:%@",[NSThread currentThread]);
NSLog(@"这是延迟操作!!!");
});
<div class="se-preview-section-delimiter"></div>
2.dispatch_once
//使用dispatch_once实现单例,以及和互斥锁实现单例的比较
(dispatch_once实现的单例比互斥锁实现的单例效率高)
// dispatch_once {}
// 1.要提供一个全局的访问点
// 2.在这个项目中,只会有一个实例对象
+(instancetype)sharedHttpTool {
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 在这里写的代码在整个项目运行过程中只会执行一次
NSLog(@"是一次吗");
instance = [[self alloc] init];
});
return instance;
}
// 使用互斥锁实现单例
+ (instancetype)sharedSync {
static id instance = nil;
@synchronized(self) {
if (instance == nil) {
instance = [[self alloc] init];
}
}
return instance;
}
<div class="se-preview-section-delimiter"></div>
8.调度组
- (void)gcdDemo1 {
// 组对象
dispatch_group_t group = dispatch_group_create();
// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"下载图片1=%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1.5];
NSLog(@"下载图片2=%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2.5];
NSLog(@"下载图片3=%@",[NSThread currentThread]);
});
// dispatch_group_notify:当组里面的所有任务执行完毕,会在主队列中添加任务.并执行任务
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"更新UI %@",[NSThread currentThread]);
});
NSLog(@"come here");
// dispatch_group_async(group, dispatch_get_main_queue(), ^{
// NSLog(@"更新UI %@",[NSThread currentThread]);;
// });
}
<div class="se-preview-section-delimiter"></div>
9.dispatch_apple的使用
NSArray *arr = @[@1,@2,@3,@4,@5];
CFTimeInterval begin1 = CFAbsoluteTimeGetCurrent();
dispatch_apply(arr.count, dispatch_get_global_queue(0, 0), ^(size_t index)
{
NSLog(@"%@,%@",arr[index],[NSThread currentThread]);
});
CFTimeInterval begin2 = CFAbsoluteTimeGetCurrent();
NSLog(@"%lf",begin1 - begin2);
<div class="se-preview-section-delimiter"></div>
- 4.Objective-C的NSOperation和NSOperationQueue(基于GCD)
###示例代码:
1.用start添加任务和开启线程
//添加任务和线程的开启
- (void)opDemo1
{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
//用start开启开启线程是在当前线程中执行任务的
[op start];
}
- (void)test
{
NSLog(@"it is test method-->%@",[NSThread currentThread]);
}
<div class="se-preview-section-delimiter"></div>
2.operation添加操作和开启线程
- (void)opDemo2
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//设置队列的并发数
//如果设置为1,那么就变成了串行队列
queue.maxConcurrentOperationCount = 2;
for (int i = 0; i < 10; i ++) {
//添加操作
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test:) object:nil];
//往队列中添加操作就-->开启了异步(除了在mainQueue中不开启子线程)
[queue addOperation:op];
}
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"这是block操作-->%@",[NSThread currentThread] );
}];
[[[NSOperationQueue alloc] init] addOperation:op];
}
<div class="se-preview-section-delimiter"></div>
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
[op start];
3.线程间通信
//线程间的通讯
- (void)opDemo3
{
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"这是耗时操作--%@",[NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"UI更新--%@",[NSThread currentThread]);
}];
}];
}
4.队列的挂起,开启以及取消队列操作
//控制队列的挂起和开启的按钮点击事件
- (IBAction)pauseAndResume {
if(self.queue.operationCount == 0) {
NSLog(@"队列中没有操作");
return;
}
// 设置队列的挂起状态
// setter = !getter
self.queue.suspended = !self.queue.isSuspended;
// 挂起队列不会影响正在执行的操作
// operationCount中包含没有执行完毕的所有操作.
// 队列如果是挂起的,再往队列中添加操作,也不会执行
if (self.queue.suspended) {
NSLog(@"暂停 = %zd",self.queue.operationCount);
} else {
NSLog(@"继续 = %zd",self.queue.operationCount);
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (int i = 0; i < 20; ++i)
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@--%d", [NSThread currentThread],i);
}];
[self.queue addOperation:op];
}
}
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
// 设置最大并发数
_queue.maxConcurrentOperationCount = 2;
}
return _queue;
}
5.任务之间添加依赖
- (void)op1
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *bop = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"111bop,%@",[NSThread currentThread]);
}];
//往bop任务中添加额外的任务
[bop addExecutionBlock:^{
NSLog(@"2222---%@",[NSThread currentThread]);
}];
NSInvocationOperation *iop = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top) object:nil];
NSInvocationOperation *iop2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top2) object:nil];
//必须先添加依赖,再把任务添加到队列中
[bop addDependency:iop];
[iop addDependency:iop2];
[queue addOperation:bop];
//添加任务到队列中
[queue addOperation:iop];
[queue addOperation:iop2];
}
以上是关于多线程以及底层实现的主要内容,如果未能解决你的问题,请参考以下文章