2015-10-26 93 views
1

在下面的代码中,当我安排新的任务(Task.Factory.StartNew)时,它将冻结UI。任何人都可以帮助我理解这里有什么问题。Task.Factory.Start新冻结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); 
} 

编辑: 更加具体......我的UI上

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

RetrieveHierarchy越来越块()方法是从缓存中加载一些数据,如果不在缓存中,然后去到DB获取数据。它与UI无关。为了证明,我在这里做的是,我从第一个任务中的缓存/数据库获取两个列表,并使用这两个值更新UI(某些树节点)。但只有当UI尝试从第一行中的RetrieveHierarchy()方法中检索值时,UI才会冻结,否则不会在其他位置冻结。

仅当数据来自数据库时才会出现此问题。一旦它被加载到缓存中,这段代码就没有时间了。

使用以下行调用ShowHierarchy()方法

ShowHierarchy(topHierarchyStrategy,bottomHierarchyStrategy);

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

+1

'RetrieveHierarchy'方法是怎样的?该实现是否会阻止UI线程?或者'SetAvailableNodes'太贵了? –

+0

因为我们没有完整的代码,我们将如何重现这一点? –

+0

尝试在[链接]看到你的答案(http://stackoverflow.com/questions/13934274/problems-with-scheduling-tasks-from-a-ui-continuewith-task) –

回答

1

鉴于你发布的代码,我不是100%确定为什么你的用户界面会锁定。以下是一些尝试解决问题的建议/提示。

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 
} 

二)在大多数情况下,你可以凑合,而无需使用TaskScheduler.FromCurrentSynchronizationContext(),使用async/await模式时。

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

+0

谢谢shaamaan ...但我不能使用异步/等待,因为我使用.NET4。0 但是,我找到了解决方案,并添加到此线程供其他人使用。 – Abhash786

+0

很高兴听到您发现了解决方案@ Abhash786。由于'async/await'现在在使用Task时非常关键,所以我建议您在将来的任何问题中都注意'asyn/await'从一开始就不可用。 ;) – Shaamaan

+0

当然...我会在下次提到这个。如果你可以查看我的解决方案并告诉我它是否看起来像你,那将是非常好的。 – Abhash786

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); 
     } 

此代码按预期工作,不会阻止用户界面。如果有人发现任何与此代码不一致或意外的情况,请让我知道。

+2

'LongRunning'不应该对此产生影响。如果在没有LongRunning的情况下使用TaskScheduler.Default,会发生什么? – svick

+0

没试过......会尽力回复你.... – Abhash786