在启动另一个块之前等待直到两个asynchronous块被执行
在使用GCD时,我们希望等到两个asynchronous块被执行完成后再继续执行下一个步骤。 什么是最好的方法来做到这一点?
我们尝试了以下,但似乎并没有工作:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ { // block1 }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ { // block2 }); // wait until both the block1 and block2 are done before start block3 // how to do that? dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ { // block3 });
使用分派组:请参阅此处的示例,在Apple的iOS开发者库的“并发编程指南”的“分派队列”一章中的“等待排队任务组”一节
你的例子可能看起来像这样:
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ { // block1 NSLog(@"Block1"); [NSThread sleepForTimeInterval:5.0]; NSLog(@"Block1 End"); }); dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ { // block2 NSLog(@"Block2"); [NSThread sleepForTimeInterval:8.0]; NSLog(@"Block2 End"); }); dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ { // block3 NSLog(@"Block3"); }); // only for non-ARC projects, handled automatically in ARC-enabled projects. dispatch_release(group);
并可以产生这样的输出:
2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1 2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2 2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End 2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End 2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3
扩展JörnEyrich的答案(如果你喜欢这个答案,请回答他的回答),如果你不能控制你的块的dispatch_async
调用,就像asynchronous完成块的情况一样,你可以使用dispatch_group_enter
和dispatch_group_leave
直。
在这个例子中,我们假装computeInBackground
是我们不能改变的东西(设想它是一个委托callback,NSURLConnection completionHandler,或其他),因此我们无法访问调度调用。
// create a group dispatch_group_t group = dispatch_group_create(); // pair a dispatch_group_enter for each dispatch_group_leave dispatch_group_enter(group); // pair 1 enter [self computeInBackground:1 completion:^{ NSLog(@"1 done"); dispatch_group_leave(group); // pair 1 leave }]; // again... (and again...) dispatch_group_enter(group); // pair 2 enter [self computeInBackground:2 completion:^{ NSLog(@"2 done"); dispatch_group_leave(group); // pair 2 leave }]; // Next, setup the code to execute after all the paired enter/leave calls. // // Option 1: Get a notification on a block that will be scheduled on the specified queue: dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSLog(@"finally!"); }); // Option 2: Block an wait for the calls to complete in code already running // (as cbartel points out, be careful with running this on the main/UI queue!): // // dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // blocks current thread // NSLog(@"finally!");
在这个例子中,computeInBackground:completion:被实现为:
- (void)computeInBackground:(int)no completion:(void (^)(void))block { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSLog(@"%d starting", no); sleep(no*2); block(); }); }
输出(带有运行时间戳):
12:57:02.574 2 starting 12:57:02.574 1 starting 12:57:04.590 1 done 12:57:06.590 2 done 12:57:06.591 finally!
另一个GCDselect是一个障碍:
dispatch_queue_t queue = dispatch_queue_create("com.company.app.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"start one!\n"); sleep(4); NSLog(@"end one!\n"); }); dispatch_async(queue, ^{ NSLog(@"start two!\n"); sleep(2); NSLog(@"end two!\n"); }); dispatch_barrier_async(queue, ^{ NSLog(@"Hi, I'm the final block!\n"); });
只要创build一个并发队列,调度你的两个块,然后派发最后一块阻挡,这将使其他两个完成。
我知道你问过GCD,但是如果你想要的话, NSOperationQueue
也可以很好地处理这种东西,例如:
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Starting 3"); }]; NSOperation *operation; operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Starting 1"); sleep(7); NSLog(@"Finishing 1"); }]; [completionOperation addDependency:operation]; [queue addOperation:operation]; operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Starting 2"); sleep(5); NSLog(@"Finishing 2"); }]; [completionOperation addDependency:operation]; [queue addOperation:operation]; [queue addOperation:completionOperation];
用Swift 3, Grand Central Dispatch提供了很多方法来解决你的问题。 根据您的需要,您可以select以下Playground片段中显示的六种模式之一。
#1。 使用DispatchGroup
, DispatchGroup
notify(qos:flags:queue:execute:)
和DispatchQueue
async(group:qos:flags:execute:)
方法
Apple开发者并发编程指南介绍了DispatchGroup
:
调度组是一种阻塞线程的方法,直到一个或多个任务完成执行。 在所有指定的任务完成之前,您可以在不能前进的地方使用此行为。 例如,在分派几个任务来计算一些数据之后,可以使用一个组来等待这些任务,然后在完成时处理结果。
import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent) let group = DispatchGroup() queue.async(group: group) { print("#1 started") Thread.sleep(forTimeInterval: 5) print("#1 finished") } queue.async(group: group) { print("#2 started") Thread.sleep(forTimeInterval: 2) print("#2 finished") } group.notify(queue: queue) { print("#3 finished") } /* prints: #1 started #2 started #2 finished #1 finished #3 finished */
#2。 使用DispatchGroup
, DispatchGroup
wait()
, DispatchGroup
enter()
和DispatchGroup
leave()
方法
import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent) let group = DispatchGroup() group.enter() queue.async { print("#1 started") Thread.sleep(forTimeInterval: 5) print("#1 finished") group.leave() } group.enter() queue.async { print("#2 started") Thread.sleep(forTimeInterval: 2) print("#2 finished") group.leave() } queue.async { group.wait() print("#3 finished") } /* prints: #1 started #2 started #2 finished #1 finished #3 finished */
请注意,您还可以将DispatchGroup
wait()
与DispatchQueue
async(group:qos:flags:execute:)
混合,或者将DispatchGroup
enter()
和DispatchGroup
leave()
与DispatchGroup
notify(qos:flags:queue:execute:)
。
#3。 使用DispatchWorkItemFlags
barrier
属性和DispatchQueue
DispatchQueue
async(group:qos:flags:execute:)
方法
来自Raywenderlich.com的Swift 3:Part 1/2文章Grand Central Dispatch教程给出了障碍的定义:
分派障碍是在处理并发队列时作为串行风格瓶颈的一组函数。 […]当您将
DispatchWorkItem
提交给DispatchWorkItem
队列时,您可以设置标志来指示它应该是在特定时间在指定队列上执行的唯一项目。 这意味着在DispatchWorkItem
执行之前,必须先完成提交到队列之前的所有项目。
用法:
import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent) queue.async { print("#1 started") Thread.sleep(forTimeInterval: 5) print("#1 finished") } queue.async { print("#2 started") Thread.sleep(forTimeInterval: 2) print("#2 finished") } queue.async(flags: .barrier) { print("#3 finished") } /* prints: #1 started #2 started #2 finished #1 finished #3 finished */
#4。 使用DispatchWorkItem
, DispatchWorkItemFlags
barrier
属性和DispatchQueue
async(execute:)
方法
import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent) queue.async { print("#1 started") Thread.sleep(forTimeInterval: 5) print("#1 finished") } queue.async { print("#2 started") Thread.sleep(forTimeInterval: 2) print("#2 finished") } let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .barrier) { print("#3 finished") } queue.async(execute: dispatchWorkItem) /* prints: #1 started #2 started #2 finished #1 finished #3 finished */
#5。 使用DispatchSemaphore
, DispatchSemaphore
wait()
和DispatchSemaphore
signal()
方法
Soroush Khanlou在GCD手册博客文章中写下了以下几行:
使用信号量,我们可以阻塞任意时间的线程,直到发送来自另一个线程的信号。 像GCD的其他部分一样,信号量是线程安全的,可以从任何地方触发。 有asynchronousAPI时,可以使用信号量进行同步,但不能对其进行修改。
Apple Developer API Reference还给出了关于DispatchSemaphore
init(value:)
DispatchSemaphore
init(value:)
的以下讨论:
当两个线程需要协调特定事件的完成时,将值传递给零值非常有用。 传递大于零的值对于pipe理池大小等于该值的有限资源池非常有用。
用法:
import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent) let semaphore = DispatchSemaphore(value: 0) queue.async { print("#1 started") Thread.sleep(forTimeInterval: 5) print("#1 finished") semaphore.signal() } queue.async { print("#2 started") Thread.sleep(forTimeInterval: 2) print("#2 finished") semaphore.signal() } queue.async { semaphore.wait() semaphore.wait() print("#3 finished") } /* prints: #1 started #2 started #2 finished #1 finished #3 finished */
#6。 使用OperationQueue
和BlockOperation
苹果开发者API参考陈述关于OperationQueue
:
操作队列使用
libdispatch
库(也称为Grand Central Dispatch)来启动其操作的执行。
用法:
import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let operationQueue = OperationQueue() let blockOne = BlockOperation { print("#1 started") Thread.sleep(forTimeInterval: 5) print("#1 finished") } let blockTwo = BlockOperation { print("#2 started") Thread.sleep(forTimeInterval: 2) print("#2 finished") } let blockThree = BlockOperation { print("#3 finished") } blockThree.addDependency(blockOne) blockThree.addDependency(blockTwo) operationQueue.addOperations([blockThree, blockTwo, blockOne], waitUntilFinished: false) /* prints: #1 started #2 started #2 finished #1 finished #3 finished or #2 started #1 started #2 finished #1 finished #3 finished */
第一个答案基本上是正确的,但是如果你想用最简单的方法来达到预期的结果,下面是一个独立的代码示例,演示如何使用信号量(这也是调度组在后台工作的方式) :
#include <dispatch/dispatch.h> #include <stdio.h> main() { dispatch_queue_t myQ = dispatch_queue_create("my.conQ", DISPATCH_QUEUE_CONCURRENT); dispatch_semaphore_t mySem = dispatch_semaphore_create(0); dispatch_async(myQ, ^{ printf("Hi I'm block one!\n"); sleep(2); dispatch_semaphore_signal(mySem);}); dispatch_async(myQ, ^{ printf("Hi I'm block two!\n"); sleep(4); dispatch_semaphore_signal(mySem);}); dispatch_async(myQ, ^{ dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER); printf("Hi, I'm the final block!\n"); }); dispatch_main(); }
上面的答案都很酷,但他们都错过了一件事。 当你使用dispatch_group_enter
/ dispatch_group_leave
时,它在它input的线程中执行任务(块)。
- (IBAction)buttonAction:(id)sender { dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT); dispatch_async(demoQueue, ^{ dispatch_group_t demoGroup = dispatch_group_create(); for(int i = 0; i < 10; i++) { dispatch_group_enter(demoGroup); [self testMethod:i block:^{ dispatch_group_leave(demoGroup); }]; } dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{ NSLog(@"All group tasks are done!"); }); }); } - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock { NSLog(@"Group task started...%ld", index); NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main"); [NSThread sleepForTimeInterval:1.f]; if(completeBlock) { completeBlock(); } }
这在创build的并发队列demoQueue
。 如果我不创build任何队列,它在主线程中运行。
- (IBAction)buttonAction:(id)sender { dispatch_group_t demoGroup = dispatch_group_create(); for(int i = 0; i < 10; i++) { dispatch_group_enter(demoGroup); [self testMethod:i block:^{ dispatch_group_leave(demoGroup); }]; } dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{ NSLog(@"All group tasks are done!"); }); } - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock { NSLog(@"Group task started...%ld", index); NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main"); [NSThread sleepForTimeInterval:1.f]; if(completeBlock) { completeBlock(); } }
还有第三种方法可以在另一个线程中执行任务:
- (IBAction)buttonAction:(id)sender { dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT); // dispatch_async(demoQueue, ^{ __weak ViewController* weakSelf = self; dispatch_group_t demoGroup = dispatch_group_create(); for(int i = 0; i < 10; i++) { dispatch_group_enter(demoGroup); dispatch_async(demoQueue, ^{ [weakSelf testMethod:i block:^{ dispatch_group_leave(demoGroup); }]; }); } dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{ NSLog(@"All group tasks are done!"); }); // }); }
当然,如上所述,您可以使用dispatch_group_async
来获得您想要的。
并不是说其他答案在某些情况下并不是很好,但是这是我一直使用Google的一个片段:
- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel { if (signInDoneSel) { [self performSelector:signInDoneSel]; } }