NSThread 需求一: 用NSThread实现三个线程任务A、B、C,它们结束之后,再执行D、E任务。 思路:用KVO监听NSThread实例的isFinished属性。
需求二: 把有三个参数的函数放在子线程执行。
NSOperation 需求一: 用NSOperation实现三个线程任务A、B、C,它们结束之后,再执行D、E任务。
方式一: 先执行封装了A + B + C操作的NSBlockOperation,KVO监听属性finished=YES时,再执行封装了D + E操作的NSBlockOperation对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 // NSBlockOperation - (void)demo { // 任务 NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work A done." , [NSThread currentThread]); }]; void(^task)(void) = ^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work B done." , [NSThread currentThread]); }; [blockOperation addExecutionBlock:task]; [blockOperation addExecutionBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work C done." , [NSThread currentThread]); }]; // 监听NSOperation状态 [blockOperation addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew context:nil]; // 队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:blockOperation]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"finished" ] && [change valueForKey:NSKeyValueChangeNewKey]) { [self performMehtod]; } } - (void)performMehtod { NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work D done." , [NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work E done." , [NSThread currentThread]); }]; [blockOperation start]; }
log信息显示如下:
1 2 3 4 5 <NSThread: 0x600001944240>{number = 3, name = (null)} work A done . <NSThread: 0x600001960100>{number = 8, name = (null)} work B done . <NSThread: 0x600001966180>{number = 9, name = (null)} work C done . <NSThread: 0x600001944240>{number = 3, name = (null)} work D done . <NSThread: 0x600001960100>{number = 8, name = (null)} work E done .
方式二: 使用操作依赖功能,依赖的底层实现就是基于KVO的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // NSBlockOperation - (void)demo { // 任务 NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work A done." , [NSThread currentThread]); }]; void(^task)(void) = ^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work B done." , [NSThread currentThread]); }; [blockOperation addExecutionBlock:task]; [blockOperation addExecutionBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work C done." , [NSThread currentThread]); }]; NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work D done." , [NSThread currentThread]); }]; [blockOperation1 addExecutionBlock:^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"%@ work E done." , [NSThread currentThread]); }]; // 通过操作依赖可以实现先后顺序 [blockOperation1 addDependency:blockOperation]; // 队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:blockOperation]; [queue addOperation:blockOperation1]; }
log信息显示如下:
1 2 3 4 5 <NSThread: 0x600000717640>{number = 5, name = (null)} work C done. <NSThread: 0x600000760700>{number = 6, name = (null)} work A done. <NSThread: 0x600000776e80>{number = 8, name = (null)} work B done. <NSThread: 0x600000776e80>{number = 8, name = (null)} work D done. <NSThread: 0x600000717640>{number = 5, name = (null)} work E done.
需求二: 把有三个参数的函数放在子线程执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 // NSInvocationOperation @interface DemoViewController () @property (nonatomic, strong) NSInvocation *invocation; @end @implementation DemoViewController - (void)viewDidLoad { [super viewDidLoad]; [self demo]; } // NSInvocation + NSMethodSignature 功能:方法的对象化,包括相关的结构信息:返回值,调用者,方法名,参数 - (void)demo { // 方法签名 NSMethodSignature *signature = [self methodSignatureForSelector:@selector(name:age:sex:)]; // NSInvocation作用是把方法对象化 self.invocation = [NSInvocation invocationWithMethodSignature:signature]; /** <NSInvocation: 0x604000471ac0> return value: {@} 0x0 target: {@} 0x0 selector: {:} null argument 2: {@} 0x0 argument 3: {@} 0x0 argument 4: {@} 0x0 */ self.invocation.target = self; self.invocation.selector = @selector(name:age:sex:); // 和签名的seletor要对应起来 // 配置参数 NSString *name = @"kobe" ; NSString *age = @"40" ; NSString *sex = @"man" ; [self.invocation setArgument:&name atIndex:2]; [self.invocation setArgument:&age atIndex:3]; [self.invocation setArgument:&sex atIndex:4]; // [self.invocation invoke]; // 调用方法 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:self.invocation]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:operation]; [operation addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew context:nil]; } - (NSString *)name:(NSString *)name age:(NSString *)age sex:(NSString *)sex { NSLog(@"name: age: sex:%@" , [NSThread currentThread]); return [NSString stringWithFormat:@"%@-%@-%@" , name, age, sex]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"finished" ] && [change valueForKey:NSKeyValueChangeNewKey]) { __unsafe_unretained NSString *returnValue; [self.invocation getReturnValue:&returnValue]; NSLog(@"returnValue:%@" , returnValue); } } @end
log信息显示如下:
1 2 name: age: sex:<NSThread: 0x600001b8cd00>{number = 6, name = (null)} returnValue:kobe-40-man
自定义NSOperation 1 2 3 4 5 @interface NSCustomOperation : NSOperation @end
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @implementation NSCustomOperation @synthesize finished = _finished; // 主要的业务逻辑放到main处理 - (void)main { if (!self.isCancelled) { for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"---%@---" , [NSThread currentThread]); } } _finished = YES; } - (void)start { // 异常处理 if (self.isFinished) { return ; } if (self.isExecuting) { return ; } [self main]; } // NSOperation对象销毁前,finished属性必须为YES,否则不销毁。 // 如果 dealloc 没被执行,是因为 _finished = NO - (void)dealloc { NSLog(@"%s" , __func__); } @end
操作依赖 可以使任务有序执行。操作依赖强大的是可以跨队列依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 - (void)method3 { // 实例化4个操作 NSBlockOperation *operationOne = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1- %@" ,[NSThread currentThread]); }]; NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2- %@" ,[NSThread currentThread]); }]; NSBlockOperation *operationThree = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"3- %@" ,[NSThread currentThread]); }]; NSBlockOperation *operationFour = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"4- %@" ,[NSThread currentThread]); }]; // 给操作添加依赖 [operationTwo addDependency:operationOne]; [operationThree addDependency:operationTwo]; [operationFour addDependency:operationThree]; // [operationOne addDependency: operationFour]; // 一定不能出现循环依赖 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:operationOne]; [[NSOperationQueue mainQueue] addOperation:operationTwo]; [queue addOperation:operationThree]; [queue addOperation:operationFour]; }
log信息显示如下:
1 2 3 4 1- <NSThread: 0x600000931700>{number = 7, name = (null)} 2- <NSThread: 0x600000900240>{number = 1, name = main} 3- <NSThread: 0x600000948380>{number = 5, name = (null)} 4- <NSThread: 0x600000948380>{number = 5, name = (null)}
1-2-3-4,任务是有序执行的,而且操作依赖更强大的是可以跨队列设置,就算第2步的操作是添加在主队列中的,结果依然有效。
线程取消 正在运行的线程不能被真正的取消,只是标识状态节点的作用。
GCD dispatch_group_t (队列组) 在组内的任务都执行完毕后,再去执行其他操作。简言之,可以获取到多个并发线程全部执行结束的时间点,汇总结果。 队列组内任务只支持异步执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 - (void)demo { NSLog(@"__BEGIN:%@__" , [NSThread currentThread]); dispatch_queue_t queue1 = dispatch_queue_create("com.test" , DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t queue2 = dispatch_queue_create("com.abc" , DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t queue3 = dispatch_queue_create("com.abcd" , DISPATCH_QUEUE_CONCURRENT); dispatch_block_t block1 = ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"----执行第一个任务---当前线程%@" ,[NSThread currentThread]); }; dispatch_block_t block2 = ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"----执行第二个任务---当前线程%@" ,[NSThread currentThread]); }; dispatch_block_t block3 = ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"----执行第三个任务---当前线程%@" ,[NSThread currentThread]); }; dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue1, block1); dispatch_group_async(group, queue2, block2); dispatch_group_async(group, queue3, block3); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"----执行最后的汇总任务---当前线程%@" ,[NSThread currentThread]); }); NSLog(@"__END:%@__" , [NSThread currentThread]); }
log信息显示如下:
1 2 3 4 5 6 __BEGIN:<NSThread: 0x600003fa8280>{number = 1, name = main}__ __END:<NSThread: 0x600003fa8280>{number = 1, name = main}__ ----执行第一个任务---当前线程<NSThread: 0x600003fc40c0>{number = 7, name = (null)} ----执行第二个任务---当前线程<NSThread: 0x600003fe8040>{number = 9, name = (null)} ----执行第三个任务---当前线程<NSThread: 0x600003fccc40>{number = 10, name = (null)} ----执行最后的汇总任务---当前线程<NSThread: 0x600003fa8280>{number = 1, name = main}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 - (void)demo { NSLog(@"__BEGIN:%@__" , [NSThread currentThread]); dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_enter(group); // 组内任务数加一 dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"----执行第一个任务---当前线程%@" , [NSThread currentThread]); dispatch_group_leave(group); // 组内任务数减一 }); dispatch_group_enter(group); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"----执行第二个任务---当前线程%@" , [NSThread currentThread]); dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"----执行第三个任务---当前线程%@" , [NSThread currentThread]); dispatch_group_leave(group); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"----执行最后的汇总任务---当前线程%@" , [NSThread currentThread]); }); NSLog(@"__END:%@__" , [NSThread currentThread]); }
log信息显示如下:
1 2 3 4 5 6 __BEGIN:<NSThread: 0x600002f14400>{number = 1, name = main}__ __END:<NSThread: 0x600002f14400>{number = 1, name = main}__ ----执行第二个任务---当前线程<NSThread: 0x600002f7cc00>{number = 8, name = (null)} ----执行第一个任务---当前线程<NSThread: 0x600002f55540>{number = 3, name = (null)} ----执行第三个任务---当前线程<NSThread: 0x600002f78800>{number = 9, name = (null)} ----执行最后的汇总任务---当前线程<NSThread: 0x600002f14400>{number = 1, name = main}
dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1。dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。当group中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。
和内存管理的引用计数类似,我们可以假设group也持有一个整形变量,当调用enter时计数加1,调用leave时计数减1,当计数为0时会调用dispatch_group_notify
以上两种基本使用方式注意事项: 添加到队列中的任务,必须是同步的,如果再开启新线程,group会失效。 如果一定要开启新线程,就搭配信号量使用,如下方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 - (void)demo { NSLog(@"__BEGIN:%@__" , [NSThread currentThread]); dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_enter(group); // 组内任务数加一 dispatch_async(queue, ^{ [self network]; dispatch_group_leave(group); // 组内任务数减一 }); dispatch_group_enter(group); dispatch_async(queue, ^{ [self network]; dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(queue, ^{ [self network]; dispatch_group_leave(group); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"----执行最后的汇总任务---当前线程%@" , [NSThread currentThread]); }); NSLog(@"__END:%@__" , [NSThread currentThread]); } - (void) network { NSString *urlstr = @"https://www.xxx.com" ; NSURL *url = [NSURL URLWithString:urlstr]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; // 信号量:异步变同步。 dispatch_semaphore_t sema = dispatch_semaphore_create(0); NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSDictionary *infoDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]; NSLog(@"%@_%@" ,[NSThread currentThread], infoDict.class); dispatch_semaphore_signal(sema); }]; [task resume]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); }
log信息显示如下:
1 2 3 4 5 6 __BEGIN:<NSThread: 0x6000036d8500>{number = 1, name = main}__ __END:<NSThread: 0x6000036d8500>{number = 1, name = main}__ <NSThread: 0x600003687b00>{number = 9, name = (null)}___NSDictionaryI <NSThread: 0x600003687b00>{number = 9, name = (null)}___NSDictionaryI <NSThread: 0x600003687a40>{number = 10, name = (null)}___NSDictionaryI ----执行最后的汇总任务---当前线程<NSThread: 0x6000036d8500>{number = 1, name = main}
dispatch_semaphore_t(信号量) 信号量,相当于NSOperationQueue中最大并发数的功能,用来控制线程的数量。示例一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // 信号量可以理解为最大并发数 具备异步变同步的功能 - (void)demo { // create的value表示,最多几个资源可访问 dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 任务1 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 1" ); sleep(1); NSLog(@"complete task 1" ); dispatch_semaphore_signal(semaphore); }); // 任务2 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 2" ); sleep(1); NSLog(@"complete task 2" ); dispatch_semaphore_signal(semaphore); }); // 任务3 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 3" ); sleep(1); NSLog(@"complete task 3" ); dispatch_semaphore_signal(semaphore); }); }
log信息显示如下:
1 2 3 4 5 6 run task 1 run task 2 complete task 2 complete task 1 run task 3 complete task 3
示例二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 - (void)demo { NSLog(@"__BEGIN:%@__" , [NSThread currentThread]); // 创建信号量,参数:信号量的初值,如果小于0则会返回NULL dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"----开始执行第一个任务---当前线程%@" ,[NSThread currentThread]); [NSThread sleepForTimeInterval:2]; NSLog(@"----结束执行第一个任务---当前线程%@" ,[NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"----开始执行第二个任务---当前线程%@" ,[NSThread currentThread]); [NSThread sleepForTimeInterval:2]; NSLog(@"----结束执行第二个任务---当前线程%@" ,[NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"----开始执行第三个任务---当前线程%@" ,[NSThread currentThread]); [NSThread sleepForTimeInterval:1]; NSLog(@"----结束执行第三个任务---当前线程%@" ,[NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); NSLog(@"__END:%@__" , [NSThread currentThread]); }
log信息显示如下:
1 2 3 4 5 6 7 8 __BEGIN:<NSThread: 0x600002d5c140>{number = 1, name = main}__ __END:<NSThread: 0x600002d5c140>{number = 1, name = main}__ ----开始执行第二个任务---当前线程<NSThread: 0x600002d18980>{number = 4, name = (null)} ----结束执行第二个任务---当前线程<NSThread: 0x600002d18980>{number = 4, name = (null)} ----开始执行第一个任务---当前线程<NSThread: 0x600002d01580>{number = 7, name = (null)} ----结束执行第一个任务---当前线程<NSThread: 0x600002d01580>{number = 7, name = (null)} ----开始执行第三个任务---当前线程<NSThread: 0x600002d62300>{number = 6, name = (null)} ----结束执行第三个任务---当前线程<NSThread: 0x600002d62300>{number = 6, name = (null)}
dispatch_barrier_async(栅栏) 在并发队列中,为了保持任务的顺序,让一部分先执行完成后才能继续进行,可以使用dispatch_barrier_async来实现。 dispatch_barrier_async 相当于一个分界线,分界线前面任务先执行,分界线里面的任务再执行,分界线后面的任务最后执行。(栅栏内部队列,可以看成串行的,依序执行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 - (void)demo { NSLog(@"__BEGIN:%@__" , [NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue" , DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"----执行第一个写入任务---当前线程%@" ,[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"----执行第二个写入任务---当前线程%@" ,[NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"----等待前面的任务完成 一阶段---当前线程%@" ,[NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"----等待前面的任务完成 二阶段---当前线程%@" ,[NSThread currentThread]); }); dispatch_async(queue, ^{ // 第一个读取任务 [NSThread sleepForTimeInterval:1]; NSLog(@"----执行第一个读取任务---当前线程%@" ,[NSThread currentThread]); }); dispatch_async(queue, ^{ // 第二个读取任务 [NSThread sleepForTimeInterval:3]; NSLog(@"----执行第二个读取任务---当前线程%@" ,[NSThread currentThread]); }); NSLog(@"__END:%@__" , [NSThread currentThread]); }
log信息显示如下:
1 2 3 4 5 6 7 8 __BEGIN:<NSThread: 0x600001ac0400>{number = 1, name = main}__ __END:<NSThread: 0x600001ac0400>{number = 1, name = main}__ ----执行第二个写入任务---当前线程<NSThread: 0x600001aa6c40>{number = 7, name = (null)} ----执行第一个写入任务---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)} ----等待前面的任务完成 一阶段---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)} ----等待前面的任务完成 二阶段---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)} ----执行第一个读取任务---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)} ----执行第二个读取任务---当前线程<NSThread: 0x600001aa6c40>{number = 7, name = (null)}
注意:dispatch_barrier_async一定要搭配自定义的队列使用,不能搭配系统提供的全局并发队列,否则dispatch_barrier_async作用和dispatch_async一样。
【dispatch_barrier_async 与 dispatch_barrier_sync异同】
共同点: 等待在它前面插入队列的任务先执行完,等待他们自己的任务执行完再执行后面的任务不同点: 1、dispatch_barrier_sync将自己内部的任务插入到队列的之后,需要把该任务执行结束之后才会继续插入后面的任务。 2、dispatch_barrier_async将自己内部的任务插入到队列之后,不会去执行任务,它会继续把后面的任务插入到队列。
用途: 在多个异步操作完成之后,统一的对非线程安全的对象进行更新操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @interface BarrierViewController () { NSMutableArray *_safeAry; dispatch_queue_t _queue; } @end @implementation BarrierViewController - (void)viewDidLoad { [super viewDidLoad]; _safeAry = [NSMutableArray array]; [_safeAry addObject:@"0" ]; [_safeAry addObject:@"1" ]; [_safeAry addObject:@"2" ]; [_safeAry addObject:@"3" ]; _queue = dispatch_queue_create("com.barrier1.concurrent" , DISPATCH_QUEUE_CONCURRENT); [self demo]; } /** 多线程来操作可变数组mutableAry,肯定会出错,因为集合都是线程不安全的。 解决方案:使用dispatch_barrier_async。 只要涉及到写操作(要做保护) */ - (void) demo { dispatch_queue_t queue = dispatch_queue_create("com.barrier2.concurrent" , DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 20; i++) { // 写 dispatch_async(queue, ^{ [self addObject:[NSString stringWithFormat:@"%d" , i + 4]]; }); // 读 dispatch_async(queue, ^{ NSLog(@"%d::%@" , i, [self indexTo:i]); }); } } // 写 保证只有一个在操作(避免了同时多个写操作导致的问题) - (void)addObject:(NSString *)object { dispatch_barrier_async(_queue, ^{ if (object != nil) { [_safeAry addObject:object]; } }); } // 注意同步,因为业务关系,必须马上返回数据 - (NSString *)indexTo:(NSInteger)index { __block NSString *result = nil; dispatch_sync(_queue, ^{ if (index < _safeAry.count) { result = _safeAry[index]; } }); return result; } @end
dispatch_apply(重复) dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API,将任务按照指定的次数追加到队列中,并等待全部队列任务执行结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (void)demo { dispatch_queue_t queue = dispatch_queue_create("com.app.concurrent" , DISPATCH_QUEUE_CONCURRENT); /** 同步队列组 @param 5 指定次数 @param queue 追加对象的Dispatch Queue @param count 带有参数的Block, count的作用是为了按执行的顺序区分各个Block */ dispatch_apply(5, queue, ^(size_t count) { NSLog(@"%zu" , count); }); NSLog(@"Done!" ); }
log信息显示如下:
如果在for循环中使用 dispatch_async 需要管理好线程的数量,否则会发生线程爆炸或死锁。而dispatch_apply是由GCD管理并发的,可以避免上述情况发生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - (void)demo { dispatch_queue_t queue = dispatch_queue_create("com.app.concurrent" , DISPATCH_QUEUE_CONCURRENT); // // 有问题的情况,可能会死锁 // for (int i = 0; i < 999 ; i++) { // dispatch_async(queue, ^(size_t i){ // NSLog(@"wrong %zu" , i); // }); // } // 正确方式 dispatch_apply(999, queue, ^(size_t i){ NSLog(@"correct %zu" , i); }); // 等待上述任务执行结束之后,当前线程才会继续往下执行。 NSLog(@"Done!" ); }
dispatch_after(延后) 1 2 3 4 5 6 7 8 9 10 11 12 - (void)demo { dispatch_queue_t queue = dispatch_queue_create("com.timer.concurrent" , DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"start:%@" , [NSThread currentThread]); dispatch_after(1, queue, ^{ NSLog(@"dispatch_after:%@" , [NSThread currentThread]); }); NSLog(@"end" ); }); }
log信息显示如下:
1 2 3 start:<NSThread: 0x6000020a1d80>{number = 7, name = (null)} end dispatch_after:<NSThread: 0x600002081380>{number = 8, name = (null)}
注意:dispatch_after原理是,先把操作添加到队列,指定时间后再执行,而不是指定时间后才把操作添加到队列。
dispatch_once 代码从程序启动就执行一次
1 2 3 4 5 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //要执行的代码块 //只会被执行一次 });
实现机制:在静态区分配内存给onceToken变量,并且初始化为0(静态区变量为全局变量,且直到程序退出才会释放onceToken),线程执行到这里时,判断onceToken的值为0,就去执行block,并且onceToken的值更改为-1,第二次再有线程来执行时,判断onceToken的值为-1,就不执行block。
苹果推荐单例的创建方式为:
1 2 3 4 5 6 7 8 + (instancetype)sharedSingleton { static id instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance; }
dispatch_queue_t激活 使用DISPATCH_QUEUE_CONCURRENT_INACTIVE创建的队列,是未激活队列,在使用的时候,需要手动激活。
1 2 3 4 5 6 7 - (void)demo { dispatch_queue_t queue = dispatch_queue_create("com.active.concurrent" , DISPATCH_QUEUE_CONCURRENT_INACTIVE); dispatch_async(queue, ^{ NSLog(@"%@" , [NSThread currentThread]); }); dispatch_activate(queue); }
log信息显示如下:
1 <NSThread: 0x600000178080>{number = 3, name = (null)}
dispatch_source 用dispatch_source实现定时器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 dispatch_source_t source ; - (void)timeSource { // 1 创建一个队列 dispatch_queue_t queue = dispatch_queue_create("com.source.serial" , DISPATCH_QUEUE_SERIAL); // io source 关联到队列 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 配置soure的时间 dispatch_source_set_timer(source , DISPATCH_TIME_NOW, 1, 1); // 配置source 的处理事件 dispatch_source_set_event_handler(source , ^{ NSLog(@"soure_event:==%@" , [NSThread currentThread]); }); // 开启定时器 dispatch_resume(source ); }
dispatch_async_f 1 2 3 4 5 6 7 8 void testMethod () { NSLog(@"testMethod::-->%@" , [NSThread currentThread]); } - (void)demo { dispatch_queue_t queue = dispatch_queue_create("com.f.concurrent" , DISPATCH_QUEUE_CONCURRENT); dispatch_async_f(queue, nil, testMethod); }
log信息显示如下:
1 testMethod::--><NSThread: 0x600003c8fa00>{number = 3, name = (null)}
僵死线程 先执行如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(ThreadOne) toTarget:self withObject:nil]; } - (void)ThreadOne { NSLog(@"start" ); [self performSelector:@selector(method_one) withObject:nil afterDelay:2]; NSLog(@"end" ); } - (void)method_one { NSLog(@"%s" , __func__); }
log信息显示如下:
根据log信息可知,method_one函数没有被执行。 原因:子线程执行完ThreadOne函数之后,任务就完成了,被回收了。不会再等待2秒钟之后去执行method_one函数了。 解决方式:开启runloop,保活线程。如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 - (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(ThreadOne) toTarget:self withObject:nil]; } - (void)ThreadOne { NSLog(@"start" ); [self performSelector:@selector(method_one) withObject:nil afterDelay:2]; NSPort *port = [[NSPort alloc] init]; [[NSRunLoop currentRunLoop] addPort:port forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; NSLog(@"end" ); } - (void)method_one { NSLog(@"%s" , __func__); }
log信息显示如下:
1 2 start -[ViewController method_one]
根据log信息可知,method_one函数被执行了。 但是有个新问题产生了,程序始终没有执行到NSLog(@”end”)代码,造成了内存泄露。 解决方式:在method_one函数中,关闭runloop。如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @interface ViewController () { NSPort *_port; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(ThreadOne) toTarget:self withObject:nil]; } - (void)ThreadOne { NSLog(@"start" ); [self performSelector:@selector(method_one) withObject:nil afterDelay:2]; _port = [[NSPort alloc] init]; [[NSRunLoop currentRunLoop] addPort:_port forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; NSLog(@"end" ); } - (void)method_one { [[NSRunLoop currentRunLoop] removePort:_port forMode:NSDefaultRunLoopMode]; NSLog(@"%s" , __func__); } @end
log信息显示如下:
1 2 3 start -[ViewController method_one] end
在A线程中开启定时器,必须在A线程中关闭,否则会造成内存泄露。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @interface ViewController () { NSTimer *_timer; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(ThreadTwo) toTarget:self withObject:nil]; } - (void)ThreadTwo { NSLog(@"start" ); _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(stopTimer) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] run]; NSLog(@"end" ); } - (void)stopTimer { NSLog(@"stopTimer" ); static int count = 0; count++; if (count > 5) { [_timer invalidate]; } } @end
log信息显示如下:
1 2 3 4 5 6 7 8 start stopTimer stopTimer stopTimer stopTimer stopTimer stopTimer end