【问题标题】:Does every WinRT/Windows Core thread have a Dispatcher?每个 WinRT/Windows Core 线程都有 Dispatcher 吗?
【发布时间】:2016-08-04 05:04:40
【问题描述】:

我们提供了一个需要在自己的自定义线程上运行代码的库。完成后,我希望这些线程通过Dispatcher (System.Windows.Threading.Dispatcher) 调用回调(事件处理程序)。库用户应使用 Dispatcher 将事件处理分派到。

我们可以简单地始终在 CoreApplication.MainView.CoreWindow.Dispatcher 上进行调度,但并非所有程序(例如 Windows 10 IoT Core 应用程序)都提供 UI,因此它们缺少主窗口。

用户可以简单地引用System.Windows.Threading.Dispatcher.CurrentDispatcher 来获取他的线程的Dispatcher 吗?或者不是所有线程都有 Dispatcher?

编辑:这里有更多关于这个问题的背景信息。希望它能让问题更容易理解:https://github.com/getsenic/nuimo-windows/issues/2

【问题讨论】:

  • 为什么要在 UI 线程上执行事件处理程序?当某些对象在后台(非 UI)线程上引发事件时,这是相当常见的情况。如果您的库的用户需要访问事件处理程序中的 UI 元素,他必须使用 Dispatcher 来处理此事件。
  • WPF 的行为相当不直观,给很多程序员带来了麻烦,这里有很多关于它的问题。 WinRT 没有重复那个错误,你只能从一个窗口中获取一个调度程序。这是合乎逻辑的方法,窗口只能在由具有调度循环的线程创建时才能工作,如果没有该循环,它就会像门钉一样死掉。如果您没有窗口,那么您也不再需要在特定线程上运行代码。
  • 有一种方法可以在 UI 线程上引发事件。但要求您的对象是从 UI 线程创建/初始化的。是你的情况吗?我的意思是它只在从 UI 线程创建对象的情况下才有效。
  • 或者例如,您可以在订阅的同一线程上引发事件处理程序,但这不是一个简单的解决方案。
  • 我已更新问题以提供更多背景信息。

标签: windows-runtime windows-10-universal windows-10-iot-core winrt-async


【解决方案1】:

首先,我不确定您是否应该在 UI 线程上执行事件处理程序,因为只有客户端知道他是否需要访问 UI 元素。

其次,在调用CoreApplication.MainView 属性之前,您可以检查CoreApplication.Views.Count > 0(我不确定它是否会起作用,因为目前我没有设备来测试它)。

您也可以通过另一种方式解决此问题:在对象的构造函数中保存正在执行的线程的 SynchronizationContext,然后使用它来引发事件。如果您的对象从 UI 线程实例化,它将起作用(在大多数情况下是这样)。这样你就可以完全拒绝 Dispatcher。

public class NotifierExample
{
    private readonly SynchronizationContext _synchronizationContext;


    public event EventHandler SomethingHappened;


    public NotifierExample()
    {
        _synchronizationContext = SynchronizationContext.Current;
    }


    public void Do()
    {
        Task.Factory.StartNew(() =>
        {
            //do something
            OnSomethingHappened();
        });
    }


    private void OnSomethingHappened()
    {
        if (_synchronizationContext != null)
        {
            _synchronizationContext.Post(o => RaiseSomethingHappened(), null);
        }
        else
        {
            RaiseSomethingHappened();
        }
    }

    private void RaiseSomethingHappened()
    {
        var somethingHappened = SomethingHappened;
        somethingHappened?.Invoke(this, EventArgs.Empty);
    }
}

【讨论】:

  • 我们不强制用户在 UI 线程上运行处理程序。但是库需要从库的事件发生在自定义线程上的事实中抽象出来——如果作为库用户,您必须将所有回调同步到主线程中以更新 UI(如果有的话),这很不方便。使用 SynchronizationContext 看起来很棒 - 将尝试一下!
【解决方案2】:

或者不是所有线程都有 Dispatcher?

调度程序线程始终与 UI 线程相关联。 IoT 无头模式应用没有 UI,因此它没有 Dispatcher 线程。

用户是否可以简单地参考 System.Windows.Threading.Dispatcher.CurrentDispatcher 来获取他线程的 Dispatcher

System.Windows.Threading.Dispatcher.CurrentDispatcher 仅在旧版 .NET 平台中受支持。正如您所指出的,UWP 替代方案是 CoreApplication.MainView.CoreWindow.Dispatcher。

如果您想在 Headless(无 GUI)模式下进行异步回调,您可以参考 Task Parallel Library(TPL)、ContinueWhenAll ContinueWhenAny 等 API... 可能很适合您的需求。参考https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.aspx

【讨论】:

  • 亲爱的杰基,感谢您的回复。不幸的是,任务不是由库用户启动的,而是由库在任何时间点启动的(它实际上是来自蓝牙设备的回调)。虽然,涉及任务并不重要,因为我的问题是关于将event Action 分派到用户希望将其分派到的线程上。具体来说就是这个问题:github.com/getSenic/nuimo-windows/issues
  • @LarsBlumberg,您是否正在考虑使用 SynchronizationContext.Post?根据无头模式,您可以将同步上下文设置为 UI 或某些非 UI 线程。
  • 看起来很棒!从哪里获得无头应用程序的SynchronizationContext
猜你喜欢
  • 1970-01-01
  • 2013-08-23
  • 1970-01-01
  • 2020-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
  • 1970-01-01
相关资源
最近更新 更多