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

发布于 2025-01-10 18:28:49 字数 980 浏览 4 评论 0原文

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

我想做的事:

// 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

扬花落满肩 2025-01-17 18:28:50

您无法直接从 perform 中调用 async 方法。它需要是同步方法,您不会等待。

你问:

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

问题是,如果您在 perform 中遇到假设的 await 挂起点,而执行路径将挂起,则线程将可以自由执行该执行器上挂起的其他代码,而“继续”(暂停点之后的代码)将稍后运行。 (await 调用与 GCD sync 调用不同,而更像是延续的调度组 notify。)如果这种情况发生在内部执行,从理论上讲,该任务的完整性可能会被其他任务破坏,这些任务可能会在延续有机会运行之前溜进去。有关这些暂停点、延续等的详细信息,请参阅Swift 并发:幕后< /a>

我建议将 doSomeLongRunningProcess 更改为不是 async,并且如果可能的话,改为使用 Task { ... } 来自 doSomeLongRunningProcess。但是您不能让此方法成为async并且具有await挂起点。

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

You cannot directly call an async method from within perform. It needs to be synchronous method, which you would not await.

You asked:

... but what if I want to call some other async function from within my doSomeLongRunningProcess?

The problem is that if you hit a hypothetical await suspension point inside perform, 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. (An await call is not like a GCD sync call, but rather more like a dispatch group notify of the continuation.) If that happens inside perform, 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 scenes

I would suggest changing doSomeLongRunningProcess to not be async, and, if possible, instead initiate any asynchronous tasks with Task { … } from within doSomeLongRunningProcess. But you can’t have this method be async and have await 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.

も星光 2025-01-17 18:28:50

我花了一段时间试图解决这个问题。我能想到的是,鉴于 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 an Actor 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文