【问题标题】:await does not resume on original contextawait 不会在原始上下文中恢复
【发布时间】:2021-03-26 04:06:37
【问题描述】:

await 出于某种原因似乎没有在调用上下文中恢复。我正在调用一个async 方法(实际上是几个async 方法的层次结构)。一切都很顺利,直到我最终到达使用 RestSharp 的ExecuteAsync() 的 DataService。最后的调用如下所示:

IRestResponse Response = await rc.ExecuteAsync(request); //rc is a RestClient object

我已经对其进行了彻底的调试,并且在每个级别上,即使在调用 ExecuteAsync 之前,以下行也会返回 true

System.Threading.Thread.CurrentThread == System.Windows.Application.Current.Dispatcher.Thread

只有在ExecuteAsync()带服务器数据返回后,上面的行才变成false,说明上面的await没有在调用线程上恢复。因此,调用此异步操作并且现在想要将返回的数据分配给 UI 的更高层会抛出臭名昭著的 STA 线程异常。

我做错了什么?我已经烧了几个小时并浏览了几篇 SO 帖子和网络文章,但这似乎不起作用。我还添加了ConfigureAwait(true) 以明确要求它在调用上下文时恢复,但它没有。

这是一个 VSTO Word 加载项。我正在将它与 WPF 一起使用。

【问题讨论】:

    标签: c# wpf async-await


    【解决方案1】:

    这是一个 VSTO Word 插件。

    这其实是问题的根源。

    await 捕获的实际“上下文”是SynchronizationContext.Current(除非它是null,在这种情况下它是TaskScheduler.Current)。 WinForms 和 WPF 等 UI 应用提供自己的 SynchronizationContext,这就是 await 在 UI 线程上恢复的方式。

    Office 加载项除外。我不知道为什么,但他们不提供SynchronizationContext。因此,如果您想在 Office 加载项中使用 await,则需要提供 SynchronizationContext

    我相信基于 WPF 的 Office 加载项将能够在每个 async 事件开始时执行此操作:

    SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
    

    但我从未真正尝试过这个。 :)

    【讨论】:

    • 两个相关问题: 1. 是否需要在每次等待之前设置同步上下文,或者我可以在加载项启动时设置一次? 2. 我能以某种方式获得 WPF 的内置/默认同步上下文,还是必须像 new DispatcherSynchronizationContext() 那样做? (我查看了库,我得到的最接近的是System.Windows.Application.Current.Dispatcher,但它不能让我访问上下文对象)。
    • 1.我认为你可以只做一次,但你需要测试一下才能确定。 2. 我不知道有什么方法可以做到这一点。但我不会担心它;上次我检查时,WPF 本身将使用多个 SyncCtx 实例,这些实例都引用同一个调度程序(每个顶级窗口 IIRC 一个)。
    • 一年后在这里。以防万一它对某人有所帮助,我已经确认您需要在每次异步操作之前设置同步上下文;所以在启动时设置一次是不够的。但是您可以创建一个全局对象(例如,ThisAddIn 类中的一个 internal 变量),然后您可以在每次调用 SynchronizationContext.SetSynchronizationContext 函数时传递它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-31
    • 2021-07-02
    • 1970-01-01
    • 1970-01-01
    • 2019-03-22
    • 2015-12-09
    • 1970-01-01
    相关资源
    最近更新 更多