ios 在启动另一个块之前等待两个异步块执行完毕

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11909629/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:41:46  来源:igfitidea点击:

Waiting until two async blocks are executed before starting another block

iosobjective-cobjective-c-blocksgrand-central-dispatch

提问by tom

When using GCD, we want to wait until two async blocks are executed and done before moving on to the next steps of execution. What is the best way to do that?

使用 GCD 时,我们希望等到两个异步块被执行并完成,然后再进行下一步的执行。最好的方法是什么?

We tried the following, but it doesn't seem to work:

我们尝试了以下方法,但似乎不起作用:

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
});

回答by J?rn Eyrich

Use dispatch groups: see herefor an example, "Waiting on Groups of Queued Tasks" in the "Dispatch Queues" chapter of Apple's iOS Developer Library's Concurrency Programming Guide

使用调度组:请参阅此处的示例,Apple 的 iOS 开发人员库并发编程指南的“调度队列”一章中的“等待队列任务组”

Your example could look something like this:

您的示例可能如下所示:

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);

and could produce output like this:

并且可以产生这样的输出:

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

回答by ?eurobur?

Expanding on J?rn Eyrich answer (upvote his answer if you upvote this one), if you do not have control over the dispatch_asynccalls for your blocks, as might be the case for async completion blocks, you can use the GCD groups using dispatch_group_enterand dispatch_group_leavedirectly.

扩展在J·RN Eyrich答案(给予好评他的回答,如果你给予好评此一个),如果你没有在控制dispatch_async你的模块调用,可能是异步完成块的情况下,你可以使用使用GCD组dispatch_group_enterdispatch_group_leave直接.

In this example, we're pretending computeInBackgroundis something we cannot change (imagine it is a delegate callback, NSURLConnection completionHandler, or whatever), and thus we don't have access to the dispatch calls.

在这个例子中,我们假装computeInBackground一些我们不能改变的东西(想象它是一个委托回调、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!");

In this example, computeInBackground:completion: is implemented as:

在此示例中,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();
    });
}

Output (with timestamps from a run):

输出(带有运行的时间戳):

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!

回答by Imanou Petit

With Swift 5.1, Grand Central Dispatchoffers many ways to solve your problem. According to your needs, you may choose one of the seven patternsshown in the following Playground snippets.

在 Swift 5.1 中,Grand Central Dispatch提供了多种方法来解决您的问题。根据您的需要,您可以选择以下 Playground 片段中显示的七种模式之一。



#1. Using DispatchGroup, DispatchGroup's notify(qos:flags:queue:execute:)and DispatchQueue's async(group:qos:flags:execute:)

#1. 使用DispatchGroupDispatchGroupnotify(qos:flags:queue:execute:)DispatchQueueasync(group:qos:flags:execute:)

The Apple Developer Concurrency Programming Guide states about DispatchGroup:

Apple Developer Concurrency Programming Guide说明DispatchGroup

Dispatch groups are a way to block a thread until one or more tasks finish executing. You can use this behavior in places where you cannot make progress until all of the specified tasks are complete. For example, after dispatching several tasks to compute some data, you might use a group to wait on those tasks and then process the results when they are done.

调度组是一种阻塞线程直到一个或多个任务完成执行的方法。您可以在所有指定任务完成之前无法取得进展的地方使用此行为。例如,在分派多个任务来计算某些数据后,您可以使用一个组来等待这些任务,然后在完成后处理结果。

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. Using DispatchGroup, DispatchGroup's wait(), DispatchGroup's enter()and DispatchGroup's leave()

#2. 使用DispatchGroup, DispatchGroup's wait(), DispatchGroup'senter()DispatchGroup'sleave()

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
 */

Note that you can also mix DispatchGroupwait()with DispatchQueueasync(group:qos:flags:execute:)or mix DispatchGroupenter()and DispatchGroupleave()with DispatchGroupnotify(qos:flags:queue:execute:).

请注意,您也可以混合DispatchGroupwait()使用DispatchQueueasync(group:qos:flags:execute:)或混合DispatchGroupenter()DispatchGroupleave()DispatchGroupnotify(qos:flags:queue:execute:)



#3. Using Dispatch?Work?Item?Flagsbarrierand DispatchQueue's async(group:qos:flags:execute:)

#3. 使用and的Dispatch?Work?Item?FlagsbarrierDispatchQueueasync(group:qos:flags:execute:)

Grand Central Dispatch Tutorial for Swift 4: Part 1/2article from Raywenderlich.com gives a definition for barriers:

Swift 4 的 Grand Central Dispatch Tutorial: Part 1/2文章来自 Raywenderlich.com 给出了障碍的定义:

Dispatch barriers are a group of functions acting as a serial-style bottleneck when working with concurrent queues. When you submit a DispatchWorkItemto a dispatch queue you can set flags to indicate that it should be the only item executed on the specified queue for that particular time. This means that all items submitted to the queue prior to the dispatch barrier must complete before the DispatchWorkItemwill execute.

调度障碍是一组在处理并发队列时充当串行风格瓶颈的函数。当您将 a 提交DispatchWorkItem到调度队列时,您可以设置标志以指示它应该是该特定时间在指定队列上执行的唯一项目。这意味着在调度障碍之前提交到队列的所有项目必须在DispatchWorkItem将执行之前完成。

Usage:

用法:

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. Using DispatchWorkItem, Dispatch?Work?Item?Flags's barrierand DispatchQueue's async(execute:)

#4. 使用DispatchWorkItemDispatch?Work?Item?FlagsbarrierDispatchQueueasync(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. Using DispatchSemaphore, DispatchSemaphore's wait()and DispatchSemaphore's signal()

#5. 使用DispatchSemaphoreDispatchSemaphorewait()DispatchSemaphoresignal()

Soroush Khanlou wrote the following lines in The GCD Handbookblog post:

Soroush Khanlou 在GCD 手册博客文章中写了以下几行:

Using a semaphore, we can block a thread for an arbitrary amount of time, until a signal from another thread is sent. Semaphores, like the rest of GCD, are thread-safe, and they can be triggered from anywhere. Semaphores can be used when there's an asynchronous API that you need to make synchronous, but you can't modify it.

使用信号量,我们可以在任意时间内阻塞一个线程,直到发送来自另一个线程的信号。信号量和 GCD 的其余部分一样,是线程安全的,可以从任何地方触发。当有异步 API 需要同步时,可以使用信号量,但不能修改它。

Apple Developer API Reference also gives the following discussion for DispatchSemaphoreinit(value:?)initializer:

Apple Developer API Reference 还对DispatchSemaphoreinit(value:?)初始化程序进行了以下讨论:

Passing zero for the value is useful for when two threads need to reconcile the completion of a particular event. Passing a value greater than zero is useful for managing a finite pool of resources, where the pool size is equal to the value.

当两个线程需要协调特定事件的完成时,为该值传递零很有用。传递大于零的值对于管理有限的资源池很有用,其中池大小等于该值。

Usage:

用法:

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. Using OperationQueueand Operation's addDependency(_:)

#6. 使用OperationQueueandOperationaddDependency(_:)

The Apple Developer API Reference states about Operation?Queue:

Apple Developer API Reference 说明了Operation?Queue

Operation queues use the libdispatchlibrary (also known as Grand Central Dispatch) to initiate the execution of their operations.

操作队列使用libdispatch库(也称为 Grand Central Dispatch)来启动其操作的执行。

Usage:

用法:

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
 */


#7. Using OperationQueueand OperationQueue's addBarrierBlock(_:)(requires iOS 13)

#7. 使用OperationQueueandOperationQueueaddBarrierBlock(_:)(需要 iOS 13)

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")
}

operationQueue.addOperations([blockTwo, blockOne], waitUntilFinished: false)
operationQueue.addBarrierBlock {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */

回答by Rob

Another GCD alternative is a barrier:

另一个 GCD 替代方案是障碍:

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");  
});

Just create a concurrent queue, dispatch your two blocks, and then dispatch the final block with barrier, which will make it wait for the other two to finish.

只需创建一个并发队列,分派您的两个块,然后用屏障分派最后一个块,这将使其等待其他两个完成。

回答by Rob

I know you asked about GCD, but if you wanted, NSOperationQueuealso handles this sort of stuff really gracefully, e.g.:

我知道你问过 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];

回答by Bruce Lee

Answers above are all cool, but they all missed one thing. group executes tasks(blocks) in the thread where it entered when you use dispatch_group_enter/dispatch_group_leave.

上面的答案都很酷,但他们都错过了一件事。group 在您使用dispatch_group_enter/时进入的线程中执行任务(块)dispatch_group_leave

- (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();
      }
    }

this runs in the created concurrent queue demoQueue. If i dont create any queue, it runs in main thread.

这在创建的并发队列中运行demoQueue。如果我不创建任何队列,它会在主线程中运行。

- (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();
      }
    }

and there's a third way to make tasks executed in another thread:

还有第三种方法可以让任务在另一个线程中执行:

- (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!");
      });
      //  });
    }

Of course, as mentioned you can use dispatch_group_asyncto get what you want.

当然,如前所述,您可以使用它dispatch_group_async来获得您想要的东西。

回答by jkh

The first answer is essentially correct, but if you want the very simplest way to accomplish the desired result, here's a stand-alone code example demonstrating how to do it with a semaphore (which is also how dispatch groups work behind the scenes, JFYI):

第一个答案基本上是正确的,但是如果您想要以最简单的方式来完成所需的结果,这里有一个独立的代码示例,演示如何使用信号量来实现(这也是调度组在幕后的工作方式,JFYI) :

#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();
}

回答by jkh

Accepted answer in swift:

迅速接受答案:

let group = DispatchGroup()

group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block1
    print("Block1")
    Thread.sleep(forTimeInterval: 5.0)
    print("Block1 End")
})


group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block2
    print("Block2")
    Thread.sleep(forTimeInterval: 8.0)
    print("Block2 End")
})

dispatch_group_notify(group, DispatchQueue.global(qos: .default), {
    // block3
    print("Block3")
})

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group)

回答by atereshkov

Swift 4.2 example:

Swift 4.2 示例:

let group = DispatchGroup.group(count: 2)
group.notify(queue: DispatchQueue.main) {
     self.renderingLine = false
     // all groups are done
}
DispatchQueue.main.async {
    self.renderTargetNode(floorPosition: targetPosition, animated: closedContour) {
        group.leave()
        // first done
    }
    self.renderCenterLine(position: targetPosition, animated: closedContour) {
        group.leave()
        // second done
    }
 }

回答by ChuckKelly

Not to say other answers are not great for certain circumstances, but this is one snippet I always user from Google:

并不是说其他​​答案在某些情况下不是很好,但这是我总是从 Google 使用的一个片段:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


    if (signInDoneSel) {
        [self performSelector:signInDoneSel];
    }

}