【问题标题】:Is it safe to store in a field the TaskScheduler of the UI Thread?将 UI Thread 的 TaskScheduler 存储在字段中是否安全?
【发布时间】:2015-02-25 16:45:28
【问题描述】:

将UI Thread的TaskScheduler存储在如下字段中是否安全:

class TaskSchedulerReference {

    private readonly _uiTaskScheduler; 

    // Constructor is called by the UI Thead 
    public TaskSchedulerReference() {
        _uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
    }

    public TaskScheduler UiTaskScheduler { get { return _uiTaskScheduler; } }
}

...这样就可以随时从Task.ContinueWith(action, TaskSchedulerReference.UiTaskScheduler)callback 中的任何后台线程通知UI。

是否保证引用在整个应用程序生命周期内保持有效?

【问题讨论】:

  • 这个静态初始化器非常危险。请参考my answer,我在这里解释了这样的事情怎么会弄乱整个应用程序。不保证该字段会在 UI 线程上初始化。
  • 请注意,这不是 SC,这是使用 SC 的TaskScheduler
  • 请忘记静态初始化器。这不是重点。我更正了这个问题...
  • @dymanoid 不是很危险 :) - 有点随机,可以在任何特定时间从任何上下文调用,失败时会杀死类 - 但其他方面完全安全。
  • @i3arnon 谢谢,我解决了这个问题

标签: c# wpf asynchronous async-await


【解决方案1】:

除了那个可疑的// This class is first visited by the UI Thread,是的。改用显式初始化程序(从 UI 线程显式运行),JIT 不能保证在 UI 线程上运行类初始化程序。

但是,我更喜欢将上下文捕获为局部变量。在多线程(和异步编程)中将任何东西暴露为全局状态是很棘手的。始终尝试尽可能使用本地状态。它也散发着旧的IsInvokeRequired 模式的味道。每个方法都应该非常明确地说明在哪里发生了什么——很容易意外引入可重入代码和死锁。

【讨论】:

  • 谢谢。我的恐惧与静态字段无关(该示例只是为了提供说明):问题是:对 SynchronizationContext 的引用可能会变得无效吗?特别是,我担心调用SynchronizationContext.SetSynchronizationContext(context),这会使存储的字段完全过时......
  • @jeromerg 是的,您不必担心在 Windows 窗体应用程序中 - 如果您有多个同步上下文,那么无论如何您都会遇到麻烦:D。但是,如果您发现自己编写了一个 Web 应用程序,它就不会工作 - 这是传递上下文而不是将其放在某个全局字段中的另一个原因。
【解决方案2】:

如果您查看code for FromCurrentSynchronizationContext,您会发现它通过捕获SynchronizationContext.Current 创建了一个SynchronizationContextTaskScheduler

只要您在 UI 线程上执行此操作,您就会捕获 UI 的 SC,而 SynchronizationContext.SetSynchronizationContext(context) 无法更改您捕获的上下文。

请注意,您存储的是一个TaskScheduler,它包含 SC 而不是 SC 本身。如果您想捕获 SC,请使用 SynchronizationContext.Current

【讨论】:

    猜你喜欢
    • 2020-12-20
    • 2021-09-26
    • 2011-01-07
    • 1970-01-01
    • 2020-10-18
    • 2020-10-30
    • 2016-05-07
    • 1970-01-01
    • 2015-06-22
    相关资源
    最近更新 更多