如何处理 Swift 中的组等待结果 3

How to handle group wait result in Swift 3

我在 playground 中尝试遵循代码,但它们似乎没有按我预期的那样工作。

两次 group_async 操作在我的 mac 上总共造成大约 5~6 秒。

我想要的是在超时时挂起群组并进行一些清理,并在群组成功完成后进行一些其他进一步的操作。任何建议表示赞赏。谢谢

import Dispatch
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true
let queue = DispatchQueue.global(qos: .utility)

func test() {
    let group = DispatchGroup()
    __dispatch_group_async(group, queue) {
        var a = [String]()
        for i in 1...999 {
            a.append(String(i))
            print("appending array a...")
        }
        print("a finished")
    }

    __dispatch_group_async(group, queue) {
        var b = [String]()
        for i in 1...999 {
            b.append(String(i))
            print("appending array b...")
        }
        print("b finished")
    }

    let result = group.wait(timeout: DispatchTime.now() + 10)
    if result == .timedOut {
        group.suspend()
        print("timed out")
    }
    print("test returns")
}


queue.async {
    test()
    print("done")
}

此代码片段提出了各种不同的问题:

  1. 我注意到 playground 的行为与您在应用程序中 运行 时的行为略有不同。我怀疑这是 PlaygroundPageneedsIndefiniteExecution 和 GCD 的一些特质。我建议在应用程序中对此进行测试。考虑到我在下面提出的要点,当我从应用程序中 运行 时,它会按预期工作。

  2. 我注意到你使用了这个模式:

    __dispatch_group_async(group, queue) {
        ...
    }
    

    我建议:

    queue.async(group: group) {
        ...
    }
    
  3. 您正在做 group.suspend()。一些注意事项:

    • 一个挂起队列,不挂起组。

    • 并且如果您曾经调用 suspend(),请确保您在某个地方有相应的调用 resume()

    • 此外,请记住 suspend() 会阻止未来的区块开始,但它不会对可能已经 运行ning 的区块做任何事情。如果您想停止已经 运行ning 的块,您可能需要取消它们。

    • 最后,请注意您只能挂起您创建的队列和源。您不能(也不应该)暂停全局队列。

  4. 我还注意到您在调度 test() 呼叫的同一个队列上使用 wait。在这种情况下,您可以摆脱它,因为它是一个并发队列,但这种模式会引发死锁。如果可以的话,我建议完全避免使用 wait,并且当然不要在调用它的同一个队列中执行它。同样,这在这里不是问题,但它是一种模式,将来可能会给您带来麻烦。

    就个人而言,我可能倾向于使用 notify 而不是 wait 来触发代码块到 运行 当两个分派块完成时。这消除了任何死锁风险。如果我想在一定时间后(即超时过程)有一段代码到 运行,我可能会使用计时器来触发一些清理过程,以防这两个代码块仍然是 运行ning(也许取消它们;参见 )。

@Rob 对每个点都有很好的细节建议。我注意到当我 运行 Evan 的代码根据 Rob 的笔记进行调整时,它似乎在 Playground 中工作。我没有在应用程序中测试过这个。请注意 group 是如何在测试函数之外声明的,因此我们稍后可以在我们可以调用 PlaygroundPage 的 finishExcution() 的地方调用 group.notify。另请注意,DispatchGroup 的通知功能是在提交的任务对象完成后执行任何其他工作的好方法。在 PlayGround 中我们调用通知:

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue.global(qos: .utility)
let group = DispatchGroup()

func test() {
      queue.async(group: group) {
         var a = [String]()
         for i in 1...999 {
            a.append(String(i))
            print("appending array a...")
         }
         print("a finished")
      }
      queue.async(group: group){
         var b = [String]()
         for i in 1...999 {
            b.append(String(i))
            print("appending array b...")
         }
         print("b finished")
      }

   DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.01) {
      print("doing clean up in timeout")
   }


}

test()
print("done")

group.notify(queue: DispatchQueue.global()) {
   print("work completed")
   PlaygroundPage.current.finishExecution()
}

只是为了比较不同的方法,请在您的 Playground 中试试这个

import Foundation

func test(timeout: Double) {
    let queue = DispatchQueue(label: "test", attributes: .concurrent)
    let group = DispatchGroup()
    var stop = false
    let delay = timeout

    queue.async(group: group) {
        var str = [String]()
        var i = 0
        while i < 1000 && !stop{
            str.append(String(i))
            i += 1
        }

        print(1, "did", i, "iterations")
    }
    queue.async(group: group) {
        var str = [String]()
        var i = 0
        while i < 2000 && !stop{
            str.append(String(i))
            i += 1
        }

        print(2, "did", i, "iterations")
    }
    queue.async(group: group) {
        var str = [String]()
        var i = 0
        while i < 100 && !stop{
            str.append(String(i))
            i += 1
        }

        print(3, "did", i, "iterations")
    }
    queue.async(group: group) {
        var str = [String]()
        var i = 0
        while i < 200 && !stop{
            str.append(String(i))
            i += 1
        }

        print(4, "did", i, "iterations")
    }
    group.wait(wallTimeout: .now() + delay)
    stop = true
    queue.sync(flags: .barrier) {} // to be sure there are no more jobs in my queue
}

var start = Date()
test(timeout: 25.0)
print("test done in", Date().timeIntervalSince(start), "from max 25.0 seconds")
print()
start = Date()
test(timeout: 5.0)
print("test done in", Date().timeIntervalSince(start), "from max 5.0 seconds")

它打印(在我的环境中)

3 did 100 iterations
4 did 200 iterations
1 did 1000 iterations
2 did 2000 iterations
test done in 17.7016019821167 from max 25.0 seconds

3 did 100 iterations
4 did 200 iterations
2 did 697 iterations
1 did 716 iterations
test done in 5.00799399614334 from max 5.0 seconds