将工作分派到多个队列并同步等待

Dispatch work to multiple queue's and wait synchronically

我希望下面的测试通过。在我的真实代码中,AsyncClass 使用 DispatchGroup class 将工作分派给多个队列。在 Swift 中,我们使用 @escaping 异步工作的完成处理程序。我正在寻找一种方法让调用线程等待 dispatchGroup 完成工作。关闭可能会以这种方式消失。

长话短说:我有一个调用线程(主线程)正在调用一个将工作分派给多个队列的函数。我希望调用线程在工作进行时被阻塞,并在工作完成后解除阻塞。这导致 @escaping completionHandler 可以消失,我可以在没有闭包的情况下正常调用该函数(当我在方法调用后转到下一行时,工作当然完成了)

我只将这段代码用于测试,我知道我不应该在生产中阻塞主线程。

用例:我有一些调用昂贵方法的测试。在本例中,该方法的作用类似于“AsyncClass”。在 AsyncClass 内部,我正在将工作分派给一些线程以加快速度。现在在测试时,我可以一直创建期望并使用闭包调用此方法,但这对我来说太冗长了。为了便于使用,我想将异步方法转换为同步方法。

import XCTest

class DispatchGroupTestTests: XCTestCase {

    func testIets() {
        let clazz = AsyncClass()

        var isCalled = false

        clazz.doSomething {
            isCalled = true
        }

        XCTAssert(isCalled)
    }

}

class AsyncClass {
    func doSomething(completionHandler: @escaping () -> ()) {
        let dispatchGroup = DispatchGroup()

        for _ in 0...5 {
            dispatchGroup.enter()

            DispatchQueue.global().async {
                let _ = (0...10000).map { [=10=] * 1000 }

                dispatchGroup.leave()
            }
        }

        dispatchGroup.wait() // Doesn't work

        dispatchGroup.notify(queue: .main) {
            completionHandler()
        }
    }
}

我设法以自旋锁的方式做到了(只是让线程在 while 循环中休眠):

import XCTest

class DispatchGroupTestTests: XCTestCase {

    func testIets() {
        let clazz = AsyncClass()

        var isCalled = false

        clazz.doSomething {
            isCalled = true
        }

        XCTAssert(isCalled)
    }

}

class AsyncClass {
    func doSomething(completionHandler: () -> ()) {
        var isDone = false
        let dispatchGroup = DispatchGroup()

        for _ in 0...5 {
            dispatchGroup.enter()

            DispatchQueue.global().async {
                let _ = (0...1000000).map { [=10=] * 10000 }

                dispatchGroup.leave()
            }
        }

        dispatchGroup.notify(queue: .global()) {
            isDone = true
        }

        while !isDone {
            print("sleepy")
            Thread.sleep(forTimeInterval: 0.1)
        }

        completionHandler()
    }
}

现在,不再需要闭包,并且可以删除期望值(虽然我在提供的示例中没有它们,但我可以在我的 'real' 测试代码中省略它们):

import XCTest

class DispatchGroupTestTests: XCTestCase {

    var called = false

    func testIets() {
        let clazz = AsyncClass()

        clazz.doSomething(called: &called)

        XCTAssert(called)
    }

}

class AsyncClass {
    func doSomething(called: inout Bool) {
        var isDone = false
        let dispatchGroup = DispatchGroup()

        for _ in 0...5 {
            dispatchGroup.enter()

            DispatchQueue.global().async {
                let _ = (0...1000000).map { [=11=] * 10000 }

                dispatchGroup.leave()
            }
        }

        dispatchGroup.notify(queue: .global()) {
            isDone = true
        }

        while !isDone {
            print("sleepy")
            Thread.sleep(forTimeInterval: 0.1)
        }

        called = true
    }
}

所以我用 measure 块做了一个性能测试,我的期望(不是 XCTestCase 期望)实现了:与分派所有工作相比,将工作分派给其他线程并在自旋锁中阻塞调用线程更快到调用线程(考虑到我们不想转义块,我们希望一切同步,只是为了在测试方法中轻松调用函数)。我实际上只是填写了随机计算,结果是这样的:

import XCTest

let work: [() -> ()] = Array.init(repeating: { let _ = (0...1000000).map {  [=12=] * 213123 / 12323 }}, count: 10)

class DispatchGroupTestTests: XCTestCase {

    func testSync() {
        measure {
            for workToDo in work {
                workToDo()
            }
        }
    }

    func testIets() {
        let clazz = AsyncClass()

        measure {
            clazz.doSomething()
        }
    }

}

class AsyncClass {
    func doSomething() {
        var isDone = false
        let dispatchGroup = DispatchGroup()

        for workToDo in work {
            dispatchGroup.enter()

            DispatchQueue.global().async {
                workToDo()

                dispatchGroup.leave()
            }
        }

        dispatchGroup.notify(queue: .global()) {
            isDone = true
        }

        while !isDone {
            Thread.sleep(forTimeInterval: 0.1)
        }
    }
}