将工作分派到多个队列并同步等待
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)
}
}
}
我希望下面的测试通过。在我的真实代码中,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)
}
}
}