- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
为什么在主线程上调用具有超过 1 个 block 的 BlockOperation 的 start() 而不是在主线程上调用其 block ?我的第一个测试总是通过,但第二个测试并非每次都通过 - 有时 block 不在主线程上执行
func test_callStartOnMainThread_executeOneBlockOnMainThread() {
let blockOper = BlockOperation {
XCTAssertTrue(Thread.isMainThread, "Expect first block was executed on Main Thread")
}
blockOper.start()
}
func test_callStartOnMainThread_executeTwoBlockOnMainThread() {
let blockOper = BlockOperation {
XCTAssertTrue(Thread.isMainThread, "Expect first block was executed on Main Thread")
}
blockOper.addExecutionBlock {
XCTAssertTrue(Thread.isMainThread, "Expect second block was executed on Main Thread")
}
blockOper.start()
}
即使下一个代码也失败
func test_callStartOnMainThread_executeTwoBlockOnMainThread() {
let asyncExpectation = expectation(description: "Async block executed")
asyncExpectation.expectedFulfillmentCount = 2
let blockOper = BlockOperation {
XCTAssertTrue(Thread.isMainThread, "Expect first block was executed on Main Thread")
asyncExpectation.fulfill()
}
blockOper.addExecutionBlock {
XCTAssertTrue(Thread.isMainThread, "Expect second block was executed on Main Thread")
asyncExpectation.fulfill()
}
OperationQueue.main.addOperation(blockOper)
wait(for: [asyncExpectation], timeout: 2.0)
}
最佳答案
正如安德烈亚斯指出的那样,the documentation warns us :
Blocks added to a block operation are dispatched with default priority to an appropriate work queue. The blocks themselves should not make any assumptions about the configuration of their execution environment.
我们启动
操作的线程以及队列的maxConcurrentOperationCount
行为是在操作级别管理的,而不是在单个执行 block 中管理的一个手术。向现有操作添加 block 与向队列添加新操作不同。操作队列管理操作之间的关系,而不是操作内的 block 之间的关系。
通过让这些 block 做一些需要一点时间的事情可以暴露这个问题。考虑一个等待一秒钟的任务(您通常永远不会 sleep
,但我们这样做只是为了模拟一个缓慢的任务并表现出相关的行为)。我还添加了必要的“兴趣点”代码,以便我们可以在 Instruments 中观看此内容,这使得更容易可视化正在发生的情况:
import os.log
let pointsOfInterest = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: .pointsOfInterest)
func someTask(_ message: String) {
let id = OSSignpostID(log: pointsOfInterest)
os_signpost(.begin, log: pointsOfInterest, name: "Block", signpostID: id, "Starting %{public}@", message)
Thread.sleep(forTimeInterval: 1)
os_signpost(.end, log: pointsOfInterest, name: "Block", signpostID: id, "Finishing %{public}@", message)
}
然后使用addExecutionBlock
:
let queue = OperationQueue() // you get same behavior if you replace these two lines with `let queue = OperationQueue.main`
queue.maxConcurrentOperationCount = 1
let operation = BlockOperation {
self.someTask("main block")
}
operation.addExecutionBlock {
self.someTask("add block 1")
}
operation.addExecutionBlock {
self.someTask("add block 2")
}
queue.addOperation(operation)
现在,我将其添加到串行操作队列中(因为您永远不会向主队列添加阻塞操作...我们需要保持该队列空闲且响应灵敏),但是如果您可以在 OperationQueue.main
上手动启动
此操作。因此,底线是,虽然 start
将“立即在当前线程中”运行操作,但使用 addExecutionBlock
添加的任何 block 都只会在“适当的线程”上并行运行。工作队列”,不一定是当前线程。
如果我们在 Instruments 中观察这一点,我们可以看到,addExecutionBlock
不仅不一定遵守启动操作的线程,而且也不遵守队列的串行性质,或者, block 并行运行:
显然,如果您将这些 block 添加为单独的操作,那么一切都很好:
for i in 1 ... 3 {
let operation = BlockOperation {
self.someTask("main block\(i)")
}
queue.addOperation(operation)
}
产量:
关于swift - 主线程上的 BlockOperation 的 start(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60302035/
我是一名优秀的程序员,十分优秀!