为了账号安全,请及时绑定邮箱和手机立即绑定

在启动另一个块之前等待直到执行两个异步块

/ 猿问

在启动另一个块之前等待直到执行两个异步块

郎朗坤 2019-07-31 14:17:28

在启动另一个块之前等待直到执行两个异步块

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


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


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

});


查看完整描述

3 回答

?
守着一只汪

使用调度组:看到这里的一个例子,在苹果的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


查看完整回答
反对 回复 2019-07-31
?
富国沪深

在乔恩Eyrich答案扩展(给予好评他的回答,如果你给予好评此一个),如果你没有在控制dispatch_async你的块调用,可能是异步完成块的情况下,您可以使用使用GCD组dispatch_group_enter和dispatch_group_leave直接。


在这个例子中,我们假装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!");

在此示例中,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!


查看完整回答
反对 回复 2019-07-31
?
慕尼黑8549860


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


#1。使用DispatchGroup,DispatchGroup's notify(qos:flags:queue:execute:)和DispatchQueue'sasync(group:qos:flags:execute:)

Apple Developer并发编程指南说明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's wait(),DispatchGroup's enter()和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

 */

请注意,您也可以混合DispatchGroup wait()使用DispatchQueue async(group:qos:flags:execute:)或混合DispatchGroup enter()并DispatchGroup leave()用DispatchGroup notify(qos:flags:queue:execute:)。


#3。使用和'sDispatchWorkItemFlags barrierDispatchQueueasync(group:qos:flags:execute:)

Swift 4的Grand Central Dispatch教程: Raywenderlich.com的第1/2部分文章给出了障碍的定义:


调度障碍是一组在使用并发队列时充当串行式瓶颈的函数。当您向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's barrier和DispatchQueue'sasync(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's wait()和DispatchSemaphore'ssignal()

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


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


Apple Developer API Reference还为DispatchSemaphore init(value:)初始化程序提供了以下讨论:


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


用法:


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和Operation'saddDependency(_:)

Apple Developer API Reference指出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

 */

#7。使用OperationQueue和OperationQueue's addBarrierBlock(_:)(需要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

 */



查看完整回答
反对 回复 2019-07-31

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信