找回密码
 会员注册
查看: 22|回复: 0

iOS多线程使用总结

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64454
发表于 2024-10-11 20:53:11 | 显示全部楼层 |阅读模式
一.概述与实现方案1. 线程与进程多线程在iOS中有着举足轻重的地位,是每一位开发者都必备的技能,当然也是面试常考的技术点,本文主要是探究我们实际开发或者面试中遇到的多线程问题。比如什么是线程?它跟进程是什么关系,队列跟线程什么关系,同步、异步、并发(并行)、串行这些概念又怎么来理解,iOS有哪些常用多线程方案,以及线程同步技术有哪些等等。线程(英语:thread)是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 --- 维基百科这里又多了一个 进程,那什么是进程呢,说白了就是是指在操作系统中正在运行的一个应用程序,如微信、支付宝app等都是一个进程。线程是就是进程的基本执行单元,一个进程的所有任务都在线程中执行。也就是说 一个进程最少要有一个线程,这个线程就是主线程。当然在我们实际使用过程中不可能只有一条主线程,我们为提高程序的执行效率,往往需要开辟多条子线程去执行一些耗时任务,这里就引出了多线程的概念。多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术根据操作系统与硬件的不同分为两类:软件多线程与硬件多线程软件多线程: 即便CPU只能运行一个线程,操作系统也可以通过快速的在不同线程之间进行切换,由于时间间隔很小,来给用户造成一种多个线程同时运行的假象硬件多线程: 如果CPU有多个核心,操作系统可以让每个核心执行一条线程,从而具有真正的同时执行多个线程的能力,当然由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。以上都是google出来的一大堆东西,比较抽象,没关系我们来看下我们实际iOS开发中用到的多线程技术。2.iOS中的多线程方案iOS 中的多线程方案主要有四种 PThread、NSThread、GCD、NSOperation,PThread 是一套纯粹C语言的API,能适用于Unix\Linux\Windows等系统,线程生命周期需要程序员自己管理,使用难度较大,在我们的实际开发中几乎用不到,在这里我们不做过多介绍,感兴趣的直接去百度。我们着重介绍另外三种方案。这里解释一下线程的生命周期,所谓的线程的生命周期就是线程从创建到死亡的过程。一般会经历:新建 - 就绪 - 运行 - 阻塞 - 死亡的过程。新建:就是初始化线程对象就绪:向线程对象发送start消息,线程对象被加入可调度线程池等待CPU调度。运行:CPU 负责调度可调度线程池中线程的执行,线程执行完成之前,状态可能会在就绪和运行之间来回切换。就绪和运行之间的状态变化由CPU负责,程序员不能干预。阻塞:当满足某个预定条件时,可以使用休眠或锁,阻塞线程执行死亡:线程执行完毕,退出,销毁。(1) NSThreadNSThread是苹果官方提供面向对象操作线程的技术,简单方便,可以直接操作线程对象,不过需要自己控制线程的生命周期,我们看下苹果官方给出的方法。[1] 初始化方法实例初始化方法- (instancetype)init API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;- (instancetype)initWithTargetid)target selectorSEL)selector objectnullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));- (instancetype)initWithBlockvoid (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));对应的初始化方法: //创建线程 NSThread *newThread = [[NSThread alloc]initWithTarget:self selectorselector(demo object"Thread"]; NSThread *newThread=[[NSThread alloc]init]; NSThread *newThread= [[NSThread alloc]initWithBlock:^{ NSLog(@"Block"); }];注意三种方法创建完成后都需要执行 [newThread start] 去启动线程。类初始化方法+ (void)detachNewThreadWithBlockvoid (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));+ (void)detachNewThreadSelectorSEL)selector toTargetid)target withObjectnullable id)argument;注意这两个类方法创建后就可执行,不需手动开启[2] 取消退出既然有了创建,那就得有退出// 实例方法 取消线程- (void)cancel;//类方法 退出+ (void)exit;[3] 线程执行状态// 线程正在执行@property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));// 线程执行结束@property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));// 线程是否可取消@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));[4] 线程间的通信方法@interface NSObject (NSThreadPerformAdditions)/** 去主线程执行指定方法* aSelector: 方法* arg: 参数* wait:表示是否等待主线程做完事情后往下走,YES表示做完后执行下面事情,NO表示跟下面事情一起执行*/- (void)performSelectorOnMainThreadSEL)aSelector withObjectnullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;/** 去指定线程执行指定方法* aSelector: 方法* arg: 参数* wait:表示是否等待本线程做完事情后往下走,YES表示做完后执行下面事,NO表示跟下面事一起执行*/- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));/** 去开启的子线程执行指定方法* SEL: 方法* arg: 参数*/- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));@end我们常说的线程间的通信所用的方法其实就是上面的这几个方法,所有继承NSObject实例化对象都可调用。当然还有其他方法也可以实现线程间的通信,如:GCD、NSOperation、NSMachPort端口等形式,我们后面用到在做介绍。举个简单的例子:我们在子线程中下载图片,然后去主线程展示:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 子线程执行下载方法 [self performSelectorInBackgroundselector(download) withObject:nil];}- (void)download{ //图片的网络路径 NSURL *url = [NSURL URLWithString"https://p3.ssl.qhimg.com/t011e94f0b9ed8e66b0.png"]; //下载图片数据 NSData *data = [NSData dataWithContentsOfURL:url]; //生成图片 UIImage *image = [UIImage imageWithData:data]; // 回主线程显示图片 [self performSelectorOnMainThreadselector(showImage withObject:image waitUntilDone:YES];}- (void)showImage:(UIImage *)image{ self.imageView.image = image;}[5] 其他常用方法+(void)currentThread 获取当前线程+(BOOL)isMultiThreaded 判断当前是否运行在子线程-(BOOL)isMainThread 判断是否在主线程+(void)sleepUntilDate:(NSDate *)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti; 当前线程休眠时间(2) GCD在介绍GCD前我们先来了解下多线程中比较容易混淆的几个概念[1]. 同步、异步、并发(并行)、串行同步和异步主要影响:能不能开启新的线程 同步:在当前线程中执行任务,不具备开启新线程的能力异步:在新的线程中执行任务,具备开启新线程的能力并发和串行主要影响:任务的执行方式 并发:也叫并行,也叫并行队列,多个任务并发(同时)执行 串行:也叫串行队列,一个任务执行完毕后,再执行下一个任务单纯的介绍概念比较抽象,我们还是结合实际使用来说明:[2] GCD 中的同步、异步方法同步执行方法:dispatch_sync()异步执行方法:dispatch_async()使用方法:dispatch_sync(dispatch_queue_tqueue,DISPATCH_NOESCAPEdispatch_block_tblock);dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);可以看到这个两个方法需要两个参数,第一个参数需要传入一个dispatch_queue_t 类型的队列,第二个是执行的block。下面介绍一下GCD的队列[3] GCD 中的队列GCD中的队列有三种:串行队列、并行队列、主队列,创建方式也非常简单:串行队列dispatch_queue_tqueue1=dispatch_queue_create("myQueue",DISPATCH_QUEUE_SERIAL);第一个参数是队列名称,第二个是一个宏定义,常用的两个宏 DISPATCH_QUEUE_SERIAL 和 DISPATCH_QUEUE_CONCURRENT分别表示串行队列和并行队列,除此之外,宏DISPATCH_QUEUE_SERIAL_INACTIVE 和 DISPATCH_QUEUE_CONCURRENT_INACTIVE 分别表示初始化的串行队列和并行队列处于不可活动状态。看下它的底层实现dispatch_queue_attr_tdispatch_queue_attr_make_initially_inactive(dispatch_queue_attr_t_Nullableattr);#defineDISPATCH_QUEUE_SERIAL_INACTIVE\dispatch_queue_attr_make_initially_inactive(DISPATCH_QUEUE_SERIAL)#defineDISPATCH_QUEUE_CONCURRENT_INACTIVE\dispatch_queue_attr_make_initially_inactive(DISPATCH_QUEUE_CONCURRENT)应当注意的是,初始化后处于不可活动状态的队列,添加到其中的任务要想开始执行,必须先调用 dispatch_activate()函数使其状态变更为可活动状态.并行队列 并行队列有两种:第一种:全局并发队列创建方法,也是系统为我们创建好的并发队列,创建方式/*-QOS_CLASS_USER_INTERACTIVE*-QOS_CLASS_USER_INITIATED*-QOS_CLASS_DEFAULT*-QOS_CLASS_UTILITY*-QOS_CLASS_BACKGROUND*///dispatch_get_global_queue(intptr_tidentifier,uintptr_tflags);dispatch_queue_tqueue=dispatch_get_global_queue(0,0);这里有两个参数,第一个参数标识线程执行优先级,第二个是苹果保留参数传参:0 就可以。第二种:手动创建并发队列//串行执行,第一个参数是名称,第二个是标识:DISPATCH_QUEUE_CONCURRENT,并发队列标识dispatch_queue_tqueue=dispatch_queue_create("myQueue",DISPATCH_QUEUE_CONCURRENT);主队列 主队列是一种特殊的串行队列dispatch_queue_tqueue=dispatch_get_main_queue();同步、异步以及队列的组合就可以实现对任务进行多线程编程的需求了。同步串行队列 dispatch_queue_t queue1 = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL); for(NSInteger i = 0; i {number = 1, name = main} i====n可以看到没有开启新的线程,都是在主线程中执行任务,并且是顺序执行的同步并行队列dispatch_queue_t queue1 = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); for(NSInteger i = 0; i {number = 1, name = main} i====n也是在主线程中顺序执行。异步串行队列dispatch_queue_t queue1 = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL); for(NSInteger i = 0; i {number = 4, name = (null)} i====0thread == {number = 5, name = (null)} i====2thread == {number = 3, name = (null)} i====3thread == {number = 6, name = (null)} i====1thread == {number = 8, name = (null)} i====5thread == {number = 7, name = (null)} i====4thread == {number = 9, name = (null)} i====6thread == {number = 10, name = (null)} i====7thread == {number = 11, name = (null)} i====8thread == {number = 5, name = (null)} i====9*/开启了多个子线程,并且是并发执行任务。注意 dispatch_async()具备开辟新线程的能力,但是不表示使用它就一定会开辟新的线程。例如 传入的 queue 是主队列,就是在主线程中执行任务,没有开辟新线程。 dispatch_queue_t queue1 = dispatch_get_main_queue(); for(NSInteger i = 0; i {number = 1, name = main} i====n主队列是一种特殊的串行队列,从打印结果看出,这里执行方式是串行,而且没有开启新的线程。具体任务的执行方式可以参考下面的表格[4] dispatch_ group_ t 队列组dispatch_group_t是一个比较实用的方法,通过构造一个组的形式,将各个同步或异步提交任务都加入到同一个组中,当所有任务都完成后会收到通知,用于进一步处理.举个简单的例子如下:dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, concurrentQueue, ^{ for (int i = 0; i {number = 5, name = (null)} // crash上面的例子可以看出,不能向当前的串行队列,同步添加任务,否则会产生死锁导致crash。线程死锁的条件:使用sync函数往当前串行队列里面添加任务,会产生死锁。(3) NSOperationNSOperation 是苹果对GCD面向对象的封装,它的底层是基于GCD实现的,相比于GCD它添加了更多实用的功能可以添加任务依赖任务执行状态的控制设置最大并发数 它有两个核心类分别是NSOperation和NSOperationQueue,NSOperation就是对任务进行的封装,封装好的任务交给不同的NSOperationQueue即可进行串行队列的执行或并发队列的执行。[1] NSOperationNSOperation 是一个抽象类,并不能直接使用,必须使用它的子类,有三种方式:NSInvocationOperation、NSBlockOperation、自定义子类继承NSOperation,前两种是苹果为我们封装好的,可以直接使用,自定义子类,需要我们实现相应的方法。NSBlockOperation & NSInvocationOperation 使用://创建一个NSBlockOperation对象,传入一个blockNSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i {number = 1, name = main} 0可以看到创建这两个任务对象去执行任务,并没有开启新线程。NSBlockOperation 相比 NSInvocationOperation 多了个addExecutionBlock 追加任务的方法, NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i {number = 6, name = (null)}task4====={number = 3, name = (null)}task1====={number = 1, name = main} 0task2====={number = 5, name = (null)}task1====={number = 1, name = main} 1task1====={number = 1, name = main} 2task1====={number = 1, name = main} 3task1====={number = 1, name = main} 4*/使用addExecutionBlock追加的任务是并发执行的,如果这个操作的任务数大于1那么会开启子线程并发执行任务,这里追加的任务不一定就是子线程,也有可能是主线程。[2] NSOperationQueueNSOperationQueue 有两种队列,一个是主队列通过[NSOperationQueue mainQueue] 获取,还有一个是自己创建的队列[[NSOperationQueue alloc] init],它同时具备并发跟串行的能力,可以通过设置最大并发数来决定。 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i {number = 4, name = (null)} 0task1====={number = 5, name = (null)} 0*/这个例子有两个任务,如果设置最大并发数为2,则会开辟两个线程,并发执行这两个任务。如果设置为1,则会在新的线程中串行执行。[3] 任务依赖addDependency可以建立两个任务之间的依赖关系,如[operation2 addDependencyperation1]; 为任务2依赖任务1,必须等任务1执行完成后才会执行任务2,看个例子 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i {number = 6, name = (null)} 0Task2===== {number = 6, name = (null)} 1Task2===== {number = 6, name = (null)} 2Task2===== {number = 6, name = (null)} 3Task2===== {number = 6, name = (null)} 4task1====={number = 6, name = (null)} 0task1====={number = 6, name = (null)} 1task1====={number = 6, name = (null)} 2task1====={number = 6, name = (null)} 3task1====={number = 6, name = (null)} 4*/还是以上面的例子,设置[operation addDependencyperation2];,可以看到任务2完成后才会执行任务1的操作。[4] 自定义NSOperation任务执行状态的控制是相对于自定义的NSOperation子类来说的。对于自定义NSOperation子类有两种类型:重写main方法 只重写operation的main方法,main方法里面写要执行的任务,系统底层控制变更任务执行完成状态,以及任务的退出。看个例子#import "TestOperation.h"@interface TestOperation ()@property (nonatomic, copy) id obj;@end@implementation TestOperation- (instancetype)initWithObject:(id)obj{ if(self = [super init]){ self.obj = obj; } return self;}- (void)main{ NSLog(@"开始执行任务%@ thread===%@",self.obj,[NSThread currentThread]);}调用 TestOperation *operation4 = [[TestOperation alloc] initWithObject:[NSString stringWithFormat"我是任务4"]]; [operation4 setCompletionBlock:^{ NSLog(@"执行完成 thread===%@",[NSThread currentThread]); }]; [operation4 start];// 打印开始执行任务我是任务4 thread==={number = 1, name = main}执行完成 thread==={number = 7, name = (null)}可以看到任务operation的main方法执行是在主线程中的,只是最后完成后的回调setCompletionBlock是异步的,好像没什么用,别着急,我们把他放入队列中执行看下,还是上面的例子,加入队列执行 NSOperationQueue *queue4 = [[NSOperationQueue alloc] init]; TestOperation *operation4 = [[TestOperation alloc] initWithObject:[NSString stringWithFormat"我是任务4"]]; TestOperation *operation5 = [[TestOperation alloc] initWithObject:[NSString stringWithFormat"我是任务5"]]; TestOperation *operation6 = [[TestOperation alloc] initWithObject:[NSString stringWithFormat"我是任务6"]]; [queue4 addOperationperation4]; [queue4 addOperationperation5]; [queue4 addOperationperation6];//打印:开始执行任务我是任务6 thread==={number = 5, name = (null)}开始执行任务我是任务4 thread==={number = 6, name = (null)}开始执行任务我是任务5 thread==={number = 7, name = (null)}这时候可以看到任务的并发执行了,operation的main方法执行结束后就会调用各自的dealloc方法进行释放,任务的生命周期结束。如果我们想让任务4、5、6 倒序执行,可以添加任务依赖 [operation4 addDependencyperation5]; [operation5 addDependencyperation6];// 打印开始执行任务我是任务6 thread==={number = 6, name = (null)}开始执行任务我是任务5 thread==={number = 6, name = (null)}开始执行任务我是任务4 thread==={number = 6, name = (null)}这样做貌似是可以的,但是如果我们的operation 中又存在异步任务(如网络请求),我们想让网络任务6请求完后调用任务5,任务5调用成功后调任务4,那该怎么办呢,我们先卖个关子,我们在第二节多个请求完成后继续进行下一个请求的方法总结中介绍。2. 重写start方法 通过重写main方法可以实现任务的串行执行,如果要让任务并发执行,就需要重写start方法。两者还是有很大区别的:如果只是重写main方法,方法执行完毕,那么整个operation就会从队列中被移除。如果你是一个自定义的operation并且它是某些类的代理,这些类恰好有异步方法,这时就会找不到代理导致程序出错了。然而start方法就算执行完毕,它的finish属性也不会变,因此你可以控制这个operation的生命周期了。然后在任务完成之后手动cancel掉这个operation即可。@interface TestStartOperation : NSOperation- (instancetype)initWithObject:(id)obj;@property (nonatomic, copy) id obj;@property (nonatomic, assign, getter=isExecuting) BOOL executing;@property (nonatomic, assign, getter=isFinished) BOOL finished;@end@implementation TestStartOperation@synthesize executing = _executing;@synthesize finished = _finished;- (instancetype)initWithObject:(id)obj{ if(self = [super init]){ self.obj = obj; } return self;}- (void)start{ //在任务开始前设置executing为YES,在此之前可能会进行一些初始化操作 self.executing = YES; NSLog(@"开始执行任务%@ thread===%@",self.obj,[NSThread currentThread]); /* 需要在适当的位置判断外部是否调用了cancel方法 如果被cancel了需要正确的结束任务 */ if (self.isCancelled) { //任务被取消正确结束前手动设置状态 self.executing = NO; self.finished = YES; return; } NSString *str = @"https://www.360.cn"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; __weak typeof(self) weakSelf = self; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // NSLog(@"response==%@",response); NSLog(@"TASK完成:====%@ thread====%@",weakSelf.obj,[NSThread currentThread]); //任务执行完成后手动设置状态 weakSelf.executing = NO; weakSelf.finished = YES; }]; [task resume];}- (void)setExecuting:(BOOL)executing{ //手动调用KVO通知 [self willChangeValueForKey"isExecuting"]; _executing = executing; //调用KVO通知 [self didChangeValueForKey:@"isExecuting"];}- (BOOL)isExecuting{ return _executing;}- (void)setFinished:(BOOL)finished{ //手动调用KVO通知 [self willChangeValueForKey:@"isFinished"]; _finished = finished; //调用KVO通知 [self didChangeValueForKey:@"isFinished"];}- (BOOL)isFinished{ return _finished;}- (BOOL)isAsynchronous{ return YES;}- (void)dealloc{ NSLog(@"Dealloc %@",self.obj);}执行与结果NSOperationQueue *queue4 = [[NSOperationQueue alloc] init];TestStartOperation *operation4 = [[TestStartOperation alloc] initWithObject:[NSString stringWithFormat:@"我是任务4"]];TestStartOperation *operation5 = [[TestStartOperation alloc] initWithObject:[NSString stringWithFormat:@"我是任务5"]];TestStartOperation *operation6 = [[TestStartOperation alloc] initWithObject:[NSString stringWithFormat:@"我是任务6"]];//设置任务依赖 [operation4 addDependencyperation5];[operation5 addDependencyperation6];[queue4 addOperationperation4];[queue4 addOperation:operation5];[queue4 addOperation:operation6];/*打印开始执行任务我是任务6 thread==={number = 6, name = (null)}TASK完成:====我是任务6 thread===={number = 8, name = (null)}开始执行任务我是任务5 thread==={number = 5, name = (null)}TASK完成:====我是任务5 thread===={number = 5, name = (null)}开始执行任务我是任务4 thread==={number = 7, name = (null)}TASK完成:====我是任务4 thread===={number = 7, name = (null)}2021-06-22 17:57:56.436591+0800 Interview01-打印[15994:9172130] Dealloc 我是任务42021-06-22 17:57:56.436690+0800 Interview01-打印[15994:9172130] Dealloc 我是任务52021-06-22 17:57:56.436784+0800 Interview01-打印[15994:9172130] Dealloc 我是任务6*/在这个例子中我们在任务请求完成后,手动设置其self.executing和self.finished状态,并且手动触发KVO,队列会监听任务的执行状态。由于我们设置了任务依赖,当任务6请求完成后才会执行任务5,任务5请求完成后 才会执行任务4。最后对各自任务进行移除队列并释放。其实这样也变相解决了上面重写main方法中无法解决的问题。二.实际应用执行多个请求完成后继续进行下一个请求的方法总结在我们的工作中经常会遇到这样的请求:一个请求依赖另一个请求的结果,或者多个请求一起发出然后再获取所有的结果后继续后续操作。根据这几种情况总结常用的方法:1. 使用GCD的dispatch_group_t实现需求:请求顺序执行,执行完成后回调结果 NSString *str = @"https://www.360.cn"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; dispatch_group_t downloadGroup = dispatch_group_create(); for (int i=0; i{number = 5, name = (null)}开始执行任务我是任务2TASK完成:====我是任务2 thread===={number = 7, name = (null)}开始执行任务我是任务1TASK完成:====我是任务1 thread===={number = 5, name = (null)}开始执行任务我是任务0TASK完成:====我是任务0 thread===={number = 6, name = (null)}*/设置任务依赖并且添加到队列后是可以满足我们的需求由于任务内部是异步回调,可以看到任务内部的执行还是依赖于dispatch_semaphore_t来实现的也可以通过重写start方法实现,在上面章节我们已经介绍过了,这里不再赘述。三. 总结本文的篇幅有点长了,但是还有一些内容没有覆盖到,比如iOS中常用的线程锁、NSOperationQueue的暂停与取消等,我们会在后面的文章中逐步完善补充。由于作者水平有限,难免出现纰漏,如有问题还请不吝赐教。参考资料:苹果官方——并发编程指南:Operation QueuesiOS GCD之dispatch_semaphore(信号量)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-27 15:03 , Processed in 0.720009 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表