Locked task.wait on a async task does not wait

  • Monday, February 13, 2012 11:54 PM
     
      Has Code

    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



    • Edited by wlf2k2 Monday, February 13, 2012 11:54 PM
    • Edited by wlf2k2 Monday, February 13, 2012 11:59 PM
    •  

All Replies

  • Tuesday, February 14, 2012 4:07 AM
    Moderator
     
     Answered

    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.

  • Tuesday, February 14, 2012 9:44 PM
     
      Has Code

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