是否可以将异步函数传递给 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()
}
}
对我来说,这似乎是不可能的,这很奇怪。我不确定 Core Data api 中是否还没有它,因为 async/await 太新了,或者是否有一个很好的理由它不可能?
像这样将 doSomeLongRunningProcess
包装在 Task
中
await moc.perform {
Task {
let obj = moc.object(with: anObjectID)
await obj.doSomeLongRunningProcess()
}
}
不起作用,因为内部 Task
在不同的线程上运行,最终会出现 CoreData 不一致的情况。我有点希望它能继承上下文的线程,但事实并非如此。
我想要一种将异步函数传递给 ManagedObjectContext
的方法,但如果做不到,我想知道为什么它不起作用?
I've looked around for a way to pass an async function to a Core Data managed object context but can't find anyway yet. I suspect there is some part of the new async/await concurrency model that I am not understanding but I have no idea what it is.
What I'd like to do:
// 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()
}
}
It seems odd to me that this is not possible. I'm not sure if it's just not there yet in the Core Data api because async/await is so new, or if there is a very good reason it's not possible?
Wrapping the doSomeLongRunningProcess
in a Task
like so
await moc.perform {
Task {
let obj = moc.object(with: anObjectID)
await obj.doSomeLongRunningProcess()
}
}
Doesn't work because the inner Task
gets run on a different thread and you end up with CoreData inconsistencies. I was kinda hoping it would inherit the context's thread but this is not the case.
I'd love a way to pass async functions to an ManagedObjectContext
, but failing that, I'd like to know why it doesn't/can't work?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您无法直接从
perform
中调用async
方法。它需要是同步方法,您不会等待。你问:
问题是,如果您在
perform
中遇到假设的await
挂起点,而执行路径将挂起,则线程将可以自由执行该执行器上挂起的其他代码,而“继续”(暂停点之后的代码)将稍后运行。 (await
调用与 GCDsync
调用不同,而更像是延续的调度组notify
。)如果这种情况发生在内部执行
,从理论上讲,该任务的完整性可能会被其他任务破坏,这些任务可能会在延续有机会运行之前溜进去。有关这些暂停点、延续等的详细信息,请参阅Swift 并发:幕后< /a>我建议将
doSomeLongRunningProcess
更改为不是async
,并且如果可能的话,改为使用Task { ... }
来自doSomeLongRunningProcess
。但是您不能让此方法成为async
并且具有await
挂起点。有关将新的异步等待模式与 Swift 并发结合使用的更多信息,请参阅 WWDC 2021 视频 将核心数据并发引入 Swift 和 SwiftUI。
You cannot directly call an
async
method from withinperform
. It needs to be synchronous method, which you would notawait
.You asked:
The problem is that if you hit a hypothetical
await
suspension point insideperform
, while path of execution would suspend, the thread would be free to execute other code pending on that executor, and the “continuation” (the code after the suspension point) would run later. (Anawait
call is not like a GCDsync
call, but rather more like a dispatch groupnotify
of the continuation.) If that happens insideperform
, the integrity of this task theoretically could be undermined by other tasks that could slip in before the continuation has a chance to run. For details on these suspension points, continuations, etc., see Swift concurrency: Behind the scenesI would suggest changing
doSomeLongRunningProcess
to not beasync
, and, if possible, instead initiate any asynchronous tasks withTask { … }
from withindoSomeLongRunningProcess
. But you can’t have this method beasync
and haveawait
suspension points.For more information about using the new async-await patterns in conjunction with Swift concurrency, see WWDC 2021 video Bring Core Data concurrency to Swift and SwiftUI.
我花了一段时间试图解决这个问题。我能想到的是,鉴于 Swift Concurrency 的当前实现,目前这是不可能的。 Rob 的回答基本上仍然是正确的,但如果您想了解更多信息:
有人在 Swift 论坛上提出了类似的问题 如何使用 Swift 并发创建具有序列化访问的对象图,其中有详细介绍。
您还不能保证异步函数将在给定的执行器上运行。全局 Actor 使之成为可能,但它将您的模型限制为单个 Actor。这意味着您不能让一个实体在主线程上调用异步函数,而另一个实体在后台线程上调用它。
即使您可以使用 Actor 来保证序列化访问,当您遇到等待暂停点时,一切都会消失,此时执行将移动。
据我所知,Apple 甚至无法将 NSManagedObjectContext 更改为 Actor,因为您仍然无法保证传递给它的异步函数不会发生变化。 t 到达等待暂停点。必须将一些基本内容添加到 Swift 并发规范中。
我的解决方案:
我基本上删除了与 NSManagedObjects 交互的所有异步/等待方法。我将重新评估 Swift Concurrency 何时/是否允许自定义执行程序等。
I've spent a while trying to work this out. Best I can figure is that it is currently impossible given the current implementation of Swift Concurrency. Rob's answer is still basically correct but if you want some more info:
There is a similar question someone has asked over on the Swift forums How to create an object graph with serialised access using Swift concurrency which goes into detail.
You can't guarantee that an async function will run on a given executor (yet). Global Actors make it sort of possible, but it limits your model to a single Actor. This means you can't have one entity calling an async function on the main thread, and another calling it on a background thread.
Even if you could use an
Actor
to guarantee serialised access, everything goes out the window when you hit an await suspension point at which point the execution will move.As far as I can tell, Apple wouldn't even be able to change
NSManagedObjectContext
to be anActor
as you still can't guarantee that async functions handed to it don't hit an await suspension point. Something fundamental would have to be added into the Swift Concurrency spec.My Solution:
I have basically removed any async/await methods that interact with NSManagedObjects. I'll reassess when/if Swift Concurrency allows for custom executors or the like.