Task.wait and continueWIth

7
我有一个类似下面的任务。

我有一个类似下面的任务。

var task = Task<string>.Factory.StartNew(() => longrunningmethod()
    .ContinueWith(a =>
             longrunningmethodcompleted((Task<string>)a,
                  TaskScheduler.FromCurrentSynchronizationContext())));

task.Wait();

我的任务将调用longrunningmethod,完成后将调用completed方法。在我的longrunningmethod中,我延迟了Thread.Sleep(30000)。当我使用Task.wait时,系统会挂起并且不会调用longrunningmethodcompleted方法。如果我不使用Task.wait,则一切顺利进行。


听起来像是死锁,你是在单线程环境下进行吗?我之前在Windows Phone开发中见过这种情况。 (相关链接:https://dev59.com/j13Va4cB1Zd3GeqPCaa-) - Benjamin Gruenbaum
3个回答

13

我强烈怀疑你的上下文是一个UI上下文。

如果是这样的话,你会导致死锁,因为你告诉longrunningmethodcompleted在当前的SynchronizationContext上执行。

然后你通过调用Wait来阻塞该上下文。由于需要在被Wait阻塞的线程上执行,所以继续执行将永远不会完成。

要解决这个问题,你不能在该上下文中使用Wait来等待继续执行。我假设longrunningmethodcompleted必须在UI线程上运行,因此解决方案是用ContinueWith替换对Wait的调用:

var ui = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task<string>.Factory.StartNew(() => longrunningmethod()
    .ContinueWith(a =>
         longrunningmethodcompleted((Task<string>)a,
         ui);
task.ContinueWith(..., ui);

或者,您可以升级到VS2012并使用async/await,这将使您编写更加简洁的代码,如下所示:

var task = Task.Run(() => longrunningmethod());
await task;
longrunningmethodcompleted(task);

谢谢大家。Stephen,它有效。我正在使用vs2010,不幸的是现在无法升级到vs2012。有没有办法让UI线程等待任务完成后再继续? - user2107843
你唯一的选择是要么在没有任务调度程序的情况下运行continuations,要么将剩余的工作作为continuation。是的,对于任何真实世界的异步场景来说,这很快就会变得非常混乱。这就是为什么发明了“async”的原因。 - Stephen Cleary

0

很难判断你的代码有什么问题,没有看到实际的异步操作,我只知道根据 MSDN 等待任务完成。你是否尝试使用当前同步上下文,导致操作被阻塞呢?

我问的原因是因为:

  1. 开始任务
  2. 等待任务完成(这是继续执行任务)
  3. 任务尝试继续使用当前同步上下文
  4. 任务尝试获取主线程
  5. 任务计划在等待完成后获取线程
  6. 但等待正在等待当前任务完成(死锁)



我的意思是,之所以你的程序可以使用 Thread.Sleep(seconds) 是因为超过时间限制后线程将继续运行。


Charles谢谢。是的,我现在明白发生了死锁。我之所以需要在continue with之后使用Task.wait,是为了使UI线程等待。是否有办法让主线程等待,直到任务和继续操作完成呢? - user2107843

-1

Thread.Sleep(nnn) 是阻塞的。使用 Task.Delay(nnn) 和 await:

await Task.Delay(30000);

编辑添加:刚注意到标签说C# 4。这需要C# 5和新的async await支持。说真的,如果你在使用异步和任务,你需要升级。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接