等待直到任务完成


99

如何使我的代码等待,直到DispatchQueue中的任务完成?是否需要任何CompletionHandler或其他东西?

func myFunction() {
    var a: Int?

    DispatchQueue.main.async {
        var b: Int = 3
        a = b
    }

    // wait until the task finishes, then print 

    print(a) // - this will contain nil, of course, because it
             // will execute before the code above

}

我正在使用Xcode 8.2并在Swift 3中编写。

Answers:


229

使用DispatchGroups可以实现这一点。您可以在群组enter()leave()通话达到平衡时得到通知:

func myFunction() {
    var a: Int?

    let group = DispatchGroup()
    group.enter()

    DispatchQueue.main.async {
        a = 1
        group.leave()
    }

    // does not wait. But the code in notify() gets run 
    // after enter() and leave() calls are balanced

    group.notify(queue: .main) {
        print(a)
    }
}

或者您可以等待:

func myFunction() {
    var a: Int?

    let group = DispatchGroup()
    group.enter()

    // avoid deadlocks by not using .main queue here
    DispatchQueue.global(attributes: .qosDefault).async {
        a = 1
        group.leave()
    }

    // wait ...
    group.wait()

    print(a) // you could also `return a` here
}

注意group.wait()阻止当前队列(在您的情况下可能是主队列),因此您必须dispatch.async在另一个队列上(如上面的示例代码中)以避免死锁


我想在另一个类中执行一个函数,但是我想等待完成该函数,然后在当前类中继续执行该操作?
Saeed Rahmatolahi

2
@SaeedRahmatolahi:要么使用该wait方法(如果您没有问题就进行阻塞,即如果您不在主线程上),要么提供完成处理程序,或者在调用类中使用notify方法。
浅思

为什么group.enter在异步块外调用?进入和离开小组不是每个街区的责任吗?
比尔

4
@Bill wait等待,直到enterleave调用平衡。如果你把enter在封闭,wait不会等待,因为enter还没有,因此被称为数量enterleave通话平衡(#进入== 0,#LEAV == 0)。
浅思

1
@rustyMagnet用于测试,这很可能不是路要走的方法。使用XCTTestExpectations代替。请参阅此示例代码
shallowThought

26

在Swift 3中,DispatchQueue完成一项任务时不需要完成处理程序。此外,您可以通过不同的方式实现目标

一种方法是:

    var a: Int?

    let queue = DispatchQueue(label: "com.app.queue")
    queue.sync {

        for  i in 0..<10 {

            print("Ⓜ️" , i)
            a = i
        }
    }

    print("After Queue \(a)")

它将等待直到循环结束,但是在这种情况下,您的主线程将阻塞。

您还可以执行以下相同操作:

    let myGroup = DispatchGroup()
    myGroup.enter()
    //// Do your task

    myGroup.leave() //// When your task completes
     myGroup.notify(queue: DispatchQueue.main) {

        ////// do your remaining work
    }

最后一件事:如果要在使用DispatchQueue完成任务时使用completionHandler,则可以使用DispatchWorkItem

这是一个示例如何使用DispatchWorkItem

let workItem = DispatchWorkItem {
    // Do something
}

let queue = DispatchQueue.global()
queue.async {
    workItem.perform()
}
workItem.notify(queue: DispatchQueue.main) {
    // Here you can notify you Main thread
}

1
我尝试了所有方法,但在尝试处理for循环中的

2

使用调度组

   dispatchGroup.enter()
   FirstOperation(completion: { _ in
dispatchGroup.leave()
  })
    dispatchGroup.enter()
    SecondOperation(completion: { _ in
dispatchGroup.leave()
  })
   dispatchGroup.wait() //Waits here on this thread until the two operations complete executing.

5
假设您在主队列上调用此方法,则将导致死锁。
shallowThought

@shallowThought非常正确。
Prateekro

我想在另一个类中执行一个函数,但是我想等待完成该函数,然后在当前类中继续执行该操作?
Saeed Rahmatolahi

1
尽管上面的示例将在主线程上阻塞,如先前的答案所示,但换行DispatchQueue.global().async{}不会阻塞主队列。
JonnyB

2

Swift 5版本的解决方案

func myCriticalFunction(){var value1:字符串?var value2:字符串?

let group = DispatchGroup()


group.enter()
//async operation 1
DispatchQueue.global(qos: .default).async { 
    // Network calls or some other async task
    value1 = //out of async task
    group.leave()
}


group.enter()
//async operation 2
DispatchQueue.global(qos: .default).async {
    // Network calls or some other async task
    value2 = //out of async task
    group.leave()
}


group.wait()

print("Value1 \(value1) , Value2 \(value2)") 

}


1

斯威夫特4

您可以在这些情况下使用异步功能。使用时DispatchGroup(),有时可能会发生死锁

var a: Int?
@objc func myFunction(completion:@escaping (Bool) -> () ) {

    DispatchQueue.main.async {
        let b: Int = 3
        a = b
        completion(true)
    }

}

override func viewDidLoad() {
    super.viewDidLoad()

    myFunction { (status) in
        if status {
            print(self.a!)
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.