task.wait on a async task does not wait
-
Monday, February 13, 2012 11:54 PM
Hi,
I don't understand why the task t2 completes immediately in StartBackgroundProcessing function. How does the task complete if the underlying async (t) lambda is awaiting? Or does async trigger another task? If I remove the async on the delegate and call int received = Receive(ct).Result instead of await, then the task t2 behaves as expected -- it does not complete until the while loop exits, but then it looks like rest of the delegate doesn't become a continuation -- i.e. thread running the StartBackgroundProcessing simply blocks doing nothing until Receive returns.
static void Main(string[] args) { CancellationTokenSource ct = new CancellationTokenSource(); Task t = StartBackgroundProcessing(ct.Token); Console.WriteLine("Press any key to quit"); Console.ReadLine(); ct.Cancel(); try { t.Wait(); } catch (AggregateException e) { Console.WriteLine(e); } } async static Task StartBackgroundProcessing(CancellationToken ct) { //do init Task<int> t1 = Task.Factory.StartNew<int>(() => { Thread.Sleep(5000); return 1; }, ct); //conitnue processing on init thread Task t2 = t1.ContinueWith( async (t)=> { data = t.Result; while (true) { if (ct.IsCancellationRequested) break; Console.WriteLine("sending {0}", data); //int received = Receive(ct).Result; int received = await Receive(ct); Console.WriteLine("receving {0}", received); //this works after await ++data; } }, ct, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Current); await t2; //this does not block on t2, so line below executes before ContinueWith lambda finishes //do cleanup Console.WriteLine("exiting BackgroundProcessing"); } async static Task<int> Receive(CancellationToken ct) { Task<int> recvTask = Task.Factory.StartNew<int>(() => { if (ct.IsCancellationRequested) return -1; Thread.Sleep(5000); return data; }, ct); return await recvTask; }
I'm trying to use async instead of spwning worker thread manually like I'm doing now:
static void Main(string[] args) { ManualResetEvent ct = new ManualResetEvent(false); Thread t = new Thread(() => { do { Thread.Sleep(20000); //initialization data = 1; Console.WriteLine("sending {0}", data); Console.WriteLine("receving {0}", data); //processing } while (!ct.WaitOne(1000)); }); t.Start(); Console.WriteLine("Press any key to quit"); Console.ReadLine(); ct.Set(); t.Join(); //cleanup }
Thank you
All Replies
-
Tuesday, February 14, 2012 4:07 AMModerator
See http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx and specifically the discussion around Unwrap. If you were to add a call to Console.WriteLine(t2.GetType()) after you create t2, I expect you'll find it's actually a Task<Task>.
I hope that helps.
- Proposed As Answer by Stephen Toub - MSFTMicrosoft Employee, Moderator Tuesday, February 14, 2012 4:07 AM
- Marked As Answer by wlf2k2 Tuesday, February 14, 2012 9:44 PM
-
Tuesday, February 14, 2012 9:44 PM
That Unwrap works, Thanks! I came across that post, but could not connect the dots. :( I read it more slowly this time...
But I'm seeing type of t2 as Task and so I could not cast it to Task<Task>. I had to explicitly call ContinueWith<Task>. Also t2.Unwrap returns me another Task<Task>. Shouldn't it return Task or perhaps that's because it's a proxy?
Task<Task> t2 = t1.ContinueWith<Task>(async (t) => { data = t.Result; while (true) { if (ct.IsCancellationRequested) break; Console.WriteLine("sending {0}", data); //int received = Receive(ct).Result; int received = await Receive(ct); Console.WriteLine("receving {0}", received); //more processing ++data; } }, ct, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Current); // await t2; Task t3 = t2.Unwrap(); await t3; //do cleanup Console.WriteLine("exiting BackgroundProcessing");

