是否可以将异步函数传递给 Swift 中的核心数据托管对象上下文?如果不是,那为什么?

Jer*_*Som 7 concurrency core-data task async-await swift

我已经寻找了一种将异步函数传递到核心数据托管对象上下文的方法,但仍然找不到。我怀疑新的异步/等待并发模型的某些部分我不理解,但我不知道它是什么。

我想做的事:

// Grab an moc
let moc = container.newBackgroundContext()

// Enter an async context
Task {

    await moc.perform {

        // Get some object
        let obj = moc.object(with: anObjectID)
        
        // This is not possible because NSManagedObjectContext.perform only accepts
        // a synchronous block
        await obj.doSomeLongRunningProcess()

    }
}
Run Code Online (Sandbox Code Playgroud)

我觉得奇怪的是这是不可能的。我不确定它是否在 Core Data api 中还没有出现,因为 async/await 太新了,或者是否有一个很好的理由它是不可能的?

像这样包裹doSomeLongRunningProcess起来Task

await moc.perform {
    Task {
        let obj = moc.object(with: anObjectID)
        await obj.doSomeLongRunningProcess()
    }
}
Run Code Online (Sandbox Code Playgroud)

不起作用,因为内部Task在不同的线程上运行,最终会导致 CoreData 不一致。我有点希望它会继承上下文的线程,但事实并非如此。

我想要一种将异步函数传递给 的方法ManagedObjectContext,但如果失败,我想知道为什么它不起作用?

Rob*_*Rob 5

async您不能直接从 内部调用方法perform。它需要是同步方法,而您不会await

\n

你问:

\n
\n

...但是如果我想从我的内部调用其他异步函数怎么办doSomeLongRunningProcess

\n
\n

问题是,如果您await在 内部遇到假设的挂起点perform,而执行路径将挂起,则线程将可以自由地执行该执行器上挂起的其他代码,并且 \xe2\x80\x9ccontinuation\xe2\x80\x9d (暂停点之后的代码)将稍后运行。(await调用不像 GCDsync调用,而更像是延续notify的调度组。)如果这种情况发生在内部perform,理论上该任务的完整性可能会被其他任务破坏,这些任务可能会在延续有机会执行任务之前溜进去。跑步。有关这些暂停点、延续等的详细信息,请参阅Swift 并发:幕后花絮

\n

我建议更改doSomeLongRunningProcess为 not be async,并且如果可能的话,改为使用Task { \xe2\x80\xa6 }from inside启动任何异步任务doSomeLongRunningProcess。但是您可以\xe2\x80\x99t 拥有此方法async并具有await暂停点。

\n

有关将新的异步等待模式与 Swift 并发结合使用的更多信息,请参阅 WWDC 2021 视频将核心数据并发引入 Swift 和 SwiftUI

\n