swift - 如何在 Swift 单元测试中强制队列不在主线程上运行
问题描述
有很多关于如何强制代码在主线程上运行的示例。我对单元测试有相反的需求。我想排除主线程从事一些工作。
为什么我需要这个:我想测试一个特定的函数是否正确运行,特别是当它同时从 2 个或更多后台线程调用时(而主线程是免费且可用的)。这是因为函数本身使用主线程,而它是一个典型的用例,它将从后台线程调用,可能是并发的。
我已经有一个测试用例,其中一个调用线程是主线程。但我也想测试主线程不忙时的情况,当2个或更多其他线程调用该函数时。
问题是似乎没有办法让主线程自由。例如,即使队列是用 定义的qoc: .background
,主线程仍然被占用:
private let queue = DispatchQueue(label: "bgqueue", qos: .background, attributes: .concurrent)
let iterations = 10
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
queue.async {
callMyFunction() // still may run on main thread, even with 1-2 iterations
}
}
我能想到的唯一方法是在同一位置阻塞所有线程(例如使用CountDownLatch),然后从所有线程继续运行,但主要是:
let latch = CountDownLatch(count: iterations)
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
latch.countdown()
latch.await()
if !Thread.isMainThread {
callMyFunction()
}
}
这里的问题是 1 - 必须确保iterations < available threads
;2 - 感觉阻塞主线程是错误的。
那么有没有更好的解决方案呢?如何在单元测试中排除主线程的工作?DispatchQueue
解决方案
只要您不分派到主队列(或其他使用主队列作为其“目标”的队列),async
就永远不会使用主线程。有一些有趣的优化sync
会影响使用哪个线程(在此不相关),但不适用于async
. 异步调度的任务将在与该 QoS 关联的工作线程上运行,而不是在主线程上运行。
所以,继续在你的测试中插入一个线程断言,以确保它不在主线程上,我想你会发现它很好:
XCTAssertFalse(Thread.isMainThread, "Shouldn't be on main thread")
话虽如此,您提到“函数本身使用主线程”这一事实。如果是这种情况,如果你的测试阻塞了主线程,你很容易死锁,等待被调用的函数也完成主线程上的处理。
您通常可以通过使用“期望”来避免这些类型的单元测试死锁。
func testWillNotDeadlock() throws {
let expectation = self.expectation(description: "testWillNotDeadlock")
someAsyncMethod {
expectation.fulfill()
}
waitForExpectations(timeout: 100)
}
在这种情况下,虽然someAsyncMethod
可能使用了主线程,但我们并没有死锁,因为我们确保测试使用了预期而不是阻塞当前线程。
因此,最重要的是,如果您正在测试一些必须使用主线程的异步方法,请确保测试不会阻塞,而是使用预期。
诚然,还有其他潜在问题的来源。例如,您调用async
100 次迭代。您需要小心这种模式,因为您可以通过这种“线程爆炸”轻松耗尽所有工作线程。如果闭concurrentPerform
包中的代码正在调用async
.
推荐阅读
- python - 在 QWebEngineView 中获取 sslerrors 信号
- javascript - 使用 JavaScript 方向 API,需要在步行和公交中混合方向指令
- html - CSS将图像与文本对齐
- javascript - 在两个分隔符上拆分字符串?
- javascript - 如何将我的菜单更改为切换菜单?
- amazon-web-services - AWS Athena 中整数数组的总和
- r - (RStudio) 带有 ggplot 的闪亮 plotOutput 不会产生任何结果
- java - 在计算大小为 K 的非连续子数组的总和时查找数组值
- c - 在 C 中使用链表时的指针问题
- elasticsearch - Kibana 在更新角色时收到此错误“不允许应用程序”?