【问题标题】:Waiting until the task finishes等到任务完成
【发布时间】:2017-02-27 11:09:38
【问题描述】:

如何让我的代码等到 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 编写代码。

【问题讨论】:

    标签: swift multithreading asynchronous swift3 grand-central-dispatch


    【解决方案1】:

    使用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 以避免死锁

    【讨论】:

    • 我想在另一个类中执行一个函数,但我想等待完成该函数,然后在当前类中继续我该如何处理?
    • @SaeedRahmatolahi: 要么使用wait 方法(如果你阻塞没有问题,即如果你不在主线程上)或提供完成处理程序或使用通知方法你的调用类。
    • 为什么在异步块外调用group.enter?进出群组不应该是每个区块的责任吗?
    • @Bill wait 等到 enterleave 呼叫平衡。如果你把enter 放在闭包中,wait 不会等待,因为enter 还没有被调用,因此enterleave 调用的数量平衡的(# enter == 0, # 离开 == 0)。
    • @rustyMagnet 进行测试,这很可能不是要走的路。请改用XCTTestExpectations。见this sample code
    【解决方案2】:

    在 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
    }
    

    【讨论】:

    • 我尝试了所有方法,但在尝试处理 for 循环中的 firebase 调用时都没有奏效
    • 这个解决方案虽然在视觉上很优雅,但不能处理超出您控制的代码异步运行的现实场景(即第一条评论中指出的 firebase 回调)。你需要.wait() 来处理更复杂的情况。
    【解决方案3】:

    Swift 5 版本的解决方案

    func myCriticalFunction() {
        var value1: String?
        var value2: String?
    
        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)") 
    }
    

    【讨论】:

      【解决方案4】:

      使用调度组

      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.
      

      【讨论】:

      • 假设你在主队列上调用这个,这会导致死锁。
      • @shallowThought 如此真实。
      • 我想在另一个类中执行一个函数,但我想等待完成该函数,然后在当前类中继续我该如何处理?
      • 虽然上面的例子会阻塞主线程,正如前面的答案所示,包裹在DispatchQueue.global().async{}中不会阻塞主队列。
      【解决方案5】:

      斯威夫特 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!)
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        不知何故,上面的 dispatchGroup enter() 和 leave() 命令不适用于我的情况。

        虽然在后台线程的 while 循环中使用 sleep(5) 对我有用。离开这里以防它帮助其他人并且它没有干扰我的其他线程。

        【讨论】:

        • sleep 是最糟糕的选择。不要那样做。绝不。它阻塞了线程。
        【解决方案7】:

        在 Swift 5.5+ 中,您可以利用 Swift Concurrency,它允许从分派到主线程的闭包中返回一个值

        func myFunction() async {
            var a : Int?
        
            a = await MainActor.run {
                let b = 3
                return b
            }
        
            print(a)
        }
        
        Task {
            await myFunction()
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-13
          相关资源
          最近更新 更多