【问题标题】:How to dispatch to main thread?如何分派到主线程?
【发布时间】:2022-11-27 13:59:26
【问题描述】:

我想在 .Net 6 中使用 C# 从主线程(UI 线程)内的非主线程执行一些代码。

我试过使用这段代码:

await Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(
    Windows.UI.Core.CoreDispatcherPriority.Normal,
    () => { }
);

这不起作用,因为 Windows.UI.Core.CoreWindow.GetForCurrentThread() 返回 null

我的第二次尝试是:

await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
    Windows.UI.Core.CoreDispatcherPriority.Normal,
    () => { }
);

这失败了,因为 Windows.ApplicationModel.Core.CoreApplication.MainView 抛出 System.InvalidOperationException

另一种方式应该是:

await System.Windows.Threading.Dispatcher.RunAsync(
    Windows.UI.Core.CoreDispatcherPriority.Normal,
    () => { }
);

但是 System.Windows.Threading 命名空间对我不可用,因为我使用的是 .Net 6,它不再受支持。

任何想法,如何从主线程(UI 线程)内的非主线程执行一些代码?

【问题讨论】:

  • 您没有指定您使用的是哪个 UI 框架。 UWP、Winforms、WPF?
  • 只是不要忽略 InvalidOperationException,它告诉你你想做的事是不可能的。猜测原因是有风险的,但可能是因为您太早启动线程并且主窗口尚未完全初始化。或者更典型的是因为您让用户在不停止线程的情况下关闭它。

标签: c# .net windows


【解决方案1】:

我想在 .Net 6 中使用 C# 从主线程(UI 线程)内的非主线程执行一些代码。

我强烈建议你.让您的 async 方法使用类似 IProgress<T> 的方法在必要时间接更新 UI 会干净得多。如果您构建代码以便主线程调用后台线程而不是后台线程通过 UI 线程操作 UI,那么您最终会得到一个更简洁的设计,您的逻辑与 UI 控件的联系更少。

也就是说,如果你真的想要,那么解决方案就是捕获UI 线程上的调度程序后台工作开始,并有后台工作使用将工作发布到 UI 线程时的调度程序(不是“当前调度程序”)。

【讨论】:

  • 即使需要 Progress 也应该是相当罕见的。在几乎所有情况下,通过任务结果返回值就足够了。有时这意味着将您的操作分解为更小的异步方法,以便可以在它们之间更新 UI,出于其他原因,这通常也是一个理想的属性。
  • IProgress<T> 是接口,不是类。并且 Progress<T> 类没有实现 Report 方法。所以我必须为我的非主线程使用IProgress<T>并自己实现Report方法。而现在,我遇到了同样的问题:它必须在主线程中运行。所以你的回答不是我的问题的答案。
  • @Willy:在主线程上创建Progress<T>。然后传给后台线程代码,它接受一个IProgress<T>类型的参数。它将为您处理线程编组。
  • @StephenCleary:如前所述,Progress<T> 没有Report 方法。所以你的解决方案也行不通。
  • @Willy - 如果您以IProgress<T> 访问它,它有一个Report 方法。当然它有一个Report方法-实现IProgress<T>
【解决方案2】:

SynchronizationContext 切换到主线程是一个很好的解决方案。但它并未针对所有 .Net 应用程序类型实施。

例如,对于控制台应用程序,没有实施解决方案。

但是对于 Windows 窗体,[WindowsFormsSynchronizationContext][2] 工作正常。

private SynchronizationContext _synchronizationContext;

里面初始化,在主线程里面调用:

_synchronizationContext = new WindowsFormsSynchronizationContext();

在此之后,您可以从不同的线程调用:

SynchronizationContext.SetSynchronizationContext(_synchronizationContext);

... here we are in a separate thread

_synchronizationContext.Post(
    (state) => {
        ... this will be executed in the main thread
    },
    null);

【讨论】:

    猜你喜欢
    • 2019-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多