【问题标题】:Why is my UI blocked when using System.Threading.Task class this way?以这种方式使用 System.Threading.Task 类时,为什么我的 UI 会被阻止?
【发布时间】:2011-05-11 18:27:47
【问题描述】:

在我的 ViewModel 中有以下代码:

Logs = new ObservableCollection<Log>();
Logs = Task.Factory.StartNew(() => mainModel.GetLogs()).Result;

Log 是一个非常简单的类,具有几个公共属性。

根据我对 Task 类的理解,以这种方式调用的 mainModel 函数 GetLogs() 应该在单独的线程上运行,并且当它从数据库中获取记录时,我的 UI 应该是响应式的,但这不是正在发生的事情,而是在正在从数据存储中获取记录,我的 UI 被阻止。

我希望有人能解释为什么……TIA。

编辑:我对 Task 类的理解不完整,使用 Task 类的 ContinueWith 方法将确保异步执行,如下面的成员回复中所述......

【问题讨论】:

    标签: wpf multithreading task


    【解决方案1】:

    这是因为您在启动异步操作后立即调用了ResultResult 属性的 getter 会阻塞当前线程的执行,直到任务完成。

    更新:

    为了异步获取结果,需要调用ContinueWith并指定任务完成时调用的函数:

    Task.Factory.StartNew(() => mainModel.GetLogs()).ContinueWith(t => Logs = t.Result);
    

    【讨论】:

    • 也就是说我应该先创建Task对象,在下一行代码调用StartNew,最后在下一行调用taskName.Result?
    • 谢谢,就是这样......我对Task类的理解显然不完整,这并不奇怪,因为我今天开始学习它:)
    【解决方案2】:

    我认为你不应该像这样使用 Result 属性,from MSDN:

    此属性的 get 访问器确保异步操作在返回之前完成。一旦计算结果可用,它就会被存储起来,并在以后调用 Result 时立即返回。

    如果您在任务运行时访问它,它会使当前线程等待。

    【讨论】:

    • 那么如何在不阻塞UI线程的情况下获得Task的Result呢?
    • 您可以将任务封装在任务本身中(这是有问题的,因为它破坏了拥有Result 的意义),或者您可以在之后使用ContinueWith 完成任务,正如 Pavlo Glazkov 所说.
    【解决方案3】:

    假设 mainModel.GetLogs 是线程安全的,您可能想要这样的东西,它在后台线程上调用 GetLogs,然后仅在完成获取日志时将 Logs 设置为结果。 TaskScheduler.FromCurrentSynchronizationContext() 确保该部分的执行在 UI 线程上运行,如果您的 UI 绑定到该集合,这将是必需的。

    Task doStuff = Task.Factory.StartNew<ICollection>(() =>
        {
            return mainModel.GetLogs();
        })
        .ContinueWith((result) =>
        {
            Logs = result;
        }, TaskScheduler.FromCurrentSynchronizationContext());
    

    【讨论】:

      猜你喜欢
      • 2021-12-20
      • 2011-03-27
      • 1970-01-01
      • 2012-06-21
      • 1970-01-01
      • 2016-07-15
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      相关资源
      最近更新 更多