Task.Factory.StartNew冻结UI

时间:2015-10-26 08:25:03

标签: c# asynchronous task-parallel-library task

在我调度新任务(Task.Factory.StartNew)的下面的代码中,它是冻结UI。任何人都可以帮我理解这里的错误。

public Task ShowHierarchy(IHierarchyFilterStrategy topHierarchyStrategy, IHierarchyFilterStrategy bottomHierarchyStrategy)
{
      IEnumerable<IHierarchyNodeViewModel> topList = null;
      IEnumerable<IHierarchyNodeViewModel> bottomList = null;
      var context = TaskScheduler.FromCurrentSynchronizationContext();
      var task = Task.Factory.StartNew(() =>
      {
          topList = topHierarchyStrategy != null ? topHierarchyStrategy.RetrieveHierarchy().ToList() : null;
          bottomList = bottomHierarchyStrategy != null
               ? bottomHierarchyStrategy.RetrieveHierarchy().ToList()
               : null;
      });

      return task.ContinueWith((antecedent) =>
      {
          View.SetAvailableNodes(topList, bottomList);
      }, context);
}

编辑: 更具体一点......我的用户界面正在阻碍

topList = topHierarchyStrategy != null ? topHierarchyStrategy.RetrieveHierarchy().ToList() : null;

RetrieveHierarchy()方法是从缓存加载一些数据,如果不在缓存中,则转到DB获取数据。它与UI无关。

总结一下,我在这里做的是,我在第一个任务和第二个任务中从缓存/ DB获取两个列表,使用这两个值来更新UI(一些树节点)。但UI只有在尝试从第一行的RetrieveHierarchy()方法中检索值时才会冻结,而不是在其他地方。

问题仅在数据来自数据库时第一次出现。一旦将其加载到缓存中,此行/没有UI冻结的时间就没有了。

使用下面的行调用ShowHierarchy()方法

ShowHierarchy(topHierarchyStrategy,bottomHierarchyStrategy);

我没有在任何地方使用它的返回值。

2 个答案:

答案 0 :(得分:2)

鉴于您发布的代码,我不能100%确定您的UI会锁定的原因。以下是尝试解决问题的一些建议/提示。

a)由于您使用的是Task课程,因此您还应该可以访问async / await个关键字。用async标记你的方法,然后在里面你可以安全地等待任务完成。

类似的东西:

public async Task ShowHierarchy(IHierarchyFilterStrategy topHierarchyStrategy, IHierarchyFilterStrategy bottomHierarchyStrategy)
{
  ...
  var topListTask = Task.Factory.StartNew(() =>
  {
    topHierarchyStrategy != null ? topHierarchyStrategy.RetrieveHierarchy().ToList() : null;
  });
   var bottomListTask = Task.Factory.StartNew(() =>
  {
    bottomList = bottomHierarchyStrategy != null
               ? bottomHierarchyStrategy.RetrieveHierarchy().ToList()
               : null;
  });
  await Task.WhenAll(topListTask, bottomListTask);
  //do things with topList, bottomList - they'll be ready at this point
}

b)在大多数情况下,使用TaskScheduler.FromCurrentSynchronizationContext()模式时,您可以在不使用async / await的情况下完成。

c)使用await关键字时,您可以从任务中获得结果。您可以重新编写代码以等待每个列表(顶部和底部)的两个不同任务。它的好处是代码更简单,更容易调试。缺点是您没有并行执行任务,因此可能需要更长时间。值得一试 - 更简单的代码更容易调试。

答案 1 :(得分:0)

要解决此问题,我需要为LongRunning提供任务创建选项。这是更新的代码...

public Task ShowHierarchy(IHierarchyFilterStrategy topHierarchyStrategy, IHierarchyFilterStrategy bottomHierarchyStrategy)
        {
            IEnumerable<IHierarchyNodeViewModel> topList = null;
            IEnumerable<IHierarchyNodeViewModel> bottomList = null;
            var context = TaskScheduler.FromCurrentSynchronizationContext();
            var options = TaskCreationOptions.LongRunning
    | TaskCreationOptions.AttachedToParent;
            var task = Task.Factory.StartNew(() =>
            {
                topList = topHierarchyStrategy != null ? topHierarchyStrategy.RetrieveHierarchy().ToList() : null;
                bottomList = bottomHierarchyStrategy != null
                    ? bottomHierarchyStrategy.RetrieveHierarchy().ToList()
                    : null;
            },CancellationToken.None,options, TaskScheduler.Default);

            return task.ContinueWith((antecedent) =>
            {
                View.SetAvailableNodes(topList, bottomList);
            }, context);
        }

此代码按预期工作,不会阻止UI。如果有人发现此代码有任何不常见/意外的情况,请告诉我。