Task.wait and continueWIth

2019-04-08 11:18发布

问题:

I am having a task like below.

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

task.Wait();

My task will call the longrunningmethod and after completing it will call completed method. Inside my longrunningmethod I am delaying by Thread.Sleep(30000). When I use Task.wait system hangs and it's not calling longrunningmethodcompleted method. If I don't use Task.wait everything flows good.

回答1:

I strongly suspect your context is a UI context.

In that case, you're causing the deadlock because you're telling longrunningmethodcompleted to execute on the current SynchronizationContext.

Then you're blocking that context by calling Wait. The continuation will never complete because it needs to execute on the thread that is blocked in Wait.

To fix this, you can't use Wait on a continuation running in that context. I'm assuming that longrunningmethodcompleted must run on the UI thread, so the solution is to replace the call to Wait with a call to ContinueWith:

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

Or, you can upgrade to VS2012 and use async/await, which lets you write much cleaner code like this:

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


回答2:

Well it is hard to tell what is wrong with your code without seeing what the actual asynch actions are, all I know is according to MSDN waits for the task to be completed. Is it possible that because you are trying to use the current SynchronizationContext your actions blocks?

The reason I am asking is because you

  1. Start the task
  2. Wait for the task to complete (which is the continue with task)
  3. Task tries to continue with current SynchronizationContext
  4. Task tries to acquire the main thread
  5. Task scheduled to take the thread after the Wait is completed
  6. But Wait is waiting on current Task to complete (deadlock)



What I mean is that the reason your program works with Thread.Sleep(seconds) is because after the time limit is up the thread will continue.



回答3:

Thread.Sleep(nnn) is blocking. Use Task.Delay(nnn) and await:

await Task.Delay(30000);

Edited to add: Just noted the tag says C# 4. This requires C# 5 and the new async await support. Seriously, if you're doing async and tasks, you need to upgrade.