You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何在主线程调用多Block的BlockOperation start()时,Block不都在主线程执行?

为什么多Block的BlockOperation在主线程调用start()时,并非所有Block都在主线程执行?

这个问题其实和BlockOperation的内部设计直接相关,咱们一步步拆解:

1. 单个Block的情况(第一个测试总是通过的原因)

当BlockOperation只包含一个执行Block时,它的start()方法会同步地在当前调用线程执行这个Block。所以你在主线程调用start(),这个Block自然就在主线程跑,断言永远通过。

2. 多个Block的情况(第二个测试偶尔失败的原因)

一旦你通过addExecutionBlock(_:)添加了额外的Block,BlockOperation的行为就变了:它的设计目标是并行执行多个Block,所以会自动把这些额外的Block提交到系统全局并发队列(也就是后台线程)去执行——哪怕你是在主线程调用start(),或者把整个BlockOperation加到OperationQueue.main里。

这里要注意:OperationQueue.main是串行队列,它只会保证BlockOperation本身的start()方法在主线程触发,但BlockOperation内部的多Block并行逻辑会绕开这个队列,自己用全局并发队列调度额外的任务,这就是为什么第二个Block偶尔会跑在后台线程,导致断言失败。

3. 解决方法:让所有Block都在主线程执行

如果你需要所有执行Block都严格在主线程运行,有两种靠谱的方式:

方式一:在每个Execution Block里手动指定主线程

直接在每个Block内部把任务派发到主线程,确保执行环境正确:

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 {
        // 手动切换到主线程执行
        DispatchQueue.main.sync {
            XCTAssertTrue(Thread.isMainThread, "Expect second block was executed on Main Thread")
            asyncExpectation.fulfill()
        }
    } 
    
    OperationQueue.main.addOperation(blockOper) 
    wait(for: [asyncExpectation], timeout: 2.0) 
}

这里用sync是为了保证Block的执行完成后再继续,避免测试期望提前等待。

方式二:放弃BlockOperation的多Block特性,直接用主队列添加单个Operation

如果不需要并行执行,直接把每个任务作为单独的Operation加到OperationQueue.main,这样所有任务都会串行在主线程执行:

func test_callStartOnMainThread_executeTwoBlockOnMainThread() { 
    let asyncExpectation = expectation(description: "Async block executed") 
    asyncExpectation.expectedFulfillmentCount = 2 
    
    let mainQueue = OperationQueue.main
    mainQueue.addOperation {
        XCTAssertTrue(Thread.isMainThread, "Expect first block was executed on Main Thread")
        asyncExpectation.fulfill()
    }
    mainQueue.addOperation {
        XCTAssertTrue(Thread.isMainThread, "Expect second block was executed on Main Thread")
        asyncExpectation.fulfill()
    }
    
    wait(for: [asyncExpectation], timeout: 2.0) 
}

总结

BlockOperation的多Block并行是设计默认行为,它优先利用全局并发队列来提升执行效率,不会继承调用线程的执行环境。如果需要所有任务都在主线程,必须手动控制每个任务的执行线程,或者改用单个Operation的方式。

内容的提问来源于stack exchange,提问作者Gulya Boiko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 16:57:51