none
Task Cancellation.Please help. RRS feed

  • Question

  • HI

    Why task cancellation exception is not thrown in below code. A child task is cancelled . My understanding is if AttachedToParent is used and if parenttask is waited then all the exception from child task can be handled in one place. So if child is throwing operationcancellationexception why no exception is thrown in below code. Please help.

    static void Main(string[] args)
            {
           
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "main");
                CancellationTokenSource cts = new CancellationTokenSource();
                CancellationToken ct = cts.Token;
    
                Task<Int32[]> parent = new Task<Int32[]>(() =>
                {
                       var results = new Int32[3]; // Create an array for the results
                                          
                        new Task(() =>
                        {
                            Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "child3");
                            Thread.Sleep(10000);
                            ct.ThrowIfCancellationRequested();
                            results[0] = Sum(30000);
                            Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "child3F");
                        }, ct, TaskCreationOptions.AttachedToParent).Start();
                        
    
                        // Returns a reference to the array (even though the elements may not be initialized yet)
                        return results;
                 
                   
                    
                },ct);
                
                
                try
                {
                    parent.Start();
                    Thread.Sleep(2000);
                    cts.Cancel();
                    parent.Wait();
    //parent waiting , so exception from attached child should be thrown and caught as aggregate exception
                    Console.ReadLine();
                }
                catch (AggregateException ag)
                {
                  AggregateException e=  ag.Flatten();
                    Console.WriteLine("test");
    
                }
            
              
    
            }
    

    Thursday, July 10, 2014 12:31 PM

All replies

  • This looks like a bug to me. I played with a simplified version of your code and it's clear that there's something wrong, for example the status of parent is not cancelled even if the child is cancelled. The documentation says that "Status of parent depends on status of child."

    You could post a bug on Connect: https://connect.microsoft.com/VisualStudio

    My simplified sample:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program {
        static void Main() {
            var cts = new CancellationTokenSource();
            var ct = cts.Token;
    
            var parent = new Task<Task>(() => {
                Console.WriteLine("parent start");
                var child = new Task(() => {
                    Console.WriteLine("child start");
                    Thread.Sleep(300);
                    ct.ThrowIfCancellationRequested();
                    Console.WriteLine("child done");
                }, ct, TaskCreationOptions.AttachedToParent);
    
                child.Start();
    
                Thread.Sleep(200);
                Console.WriteLine("parent done");
    
                return child;
            }, ct);
    
            try {
                parent.Start();
                Thread.Sleep(100);
                cts.Cancel();
                parent.Wait();
                // this will print false true, that's unexpected
                Console.WriteLine("{0} {1}", parent.IsCanceled, parent.Result.IsCanceled);
            }
            catch (AggregateException ag) {
                Console.WriteLine(ag.Flatten());
            }
        }
    }
    

    Thursday, July 10, 2014 1:35 PM
    Moderator
  • It works the way you describes when an exception is thrown from the child task, e.g.:

    new Task(() =>
        {
         Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "child3");
         Thread.Sleep(10000);
         throw new Exception("test");
         results[0] = Sum(30000);
         Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "child3F");
        }, ct, TaskCreationOptions.AttachedToParent).Start();

    However, calling the ThrowIfCancellationRequested() method is functionality equivalent to:

    if (token.IsCancellationRequested) 
        throw new OperationCanceledException(token);

    If you want to be able to catch the OperationCanceledException that is thrown, you could observe the CancellationToken by passing it to the Wait method:

    try
    			{
    				parent.Start();
    				Thread.Sleep(2000);
    				cts.Cancel();
    				parent.Wait(ct);
    				//parent waiting , so exception from attached child should be thrown and caught as aggregate exception
    				Console.ReadLine();
    			}
    			catch (AggregateException ag)
    			{
    				AggregateException e = ag.Flatten();
    				Console.WriteLine("test");
    			}
    			catch (OperationCanceledException operationCanceledException)
    			{
    				Console.WriteLine("cancelled");
    			}

    Then you will be an OperationCanceledException directly when the Cancel() is called.

    Alternatively, you could throw an OperationCanceledException manually from within the child task:

    Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "main");
       CancellationTokenSource cts = new CancellationTokenSource();
       CancellationToken ct = cts.Token;
       Task child = null;
       Task<Int32[]> parent = new Task<Int32[]>(() =>
       {
        var results = new Int32[3]; // Create an array for the results
    
        new Task(() =>
        {
         Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "child3");
         Thread.Sleep(10000);
         if(ct.IsCancellationRequested)
          throw new OperationCanceledException();
         results[0] = Sum(30000);
         Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + "child3F");
        }, ct, TaskCreationOptions.AttachedToParent).Start();
    
    
        // Returns a reference to the array (even though the elements may not be initialized yet)
        return results;
    
       }, ct);
    
    
    
       try
       {
        parent.Start();
        Thread.Sleep(2000);
        cts.Cancel();
        parent.Wait();
        //parent waiting , so exception from attached child should be thrown and caught as aggregate exception
        Console.ReadLine();
       }
       catch (AggregateException ag)
       {
        AggregateException e = ag.Flatten();
        Console.WriteLine("test");
       }
    
    


    Thursday, July 10, 2014 1:58 PM
  • Thanks Mike for sharing your opinion about the issue.
    Friday, July 11, 2014 9:39 AM
  • Magnus.

    I disagree with your statement which says

    "It works the way you describes when an exception is thrown from the child task.If you want to be able to catch the OperationCanceledException that is thrown, you could observe the CancellationToken by passing it to the Wait method".

    Even if I call Parent.Wait(ct), exception is thrown as soon as you hit the line Parent.Wait(ct) and operationcancellationexception is throw.  This error doesn't corrospond to the error thrown by child task.Till this point child has not even thrown the cancellationexception. Also , please not status of parenttask is still shown as running. Thus neither child nor parent is changing its status.

    Friday, July 11, 2014 9:44 AM
  • Hi LearnerPR,

    If a nested task throws an exception, it must be observed or handled directly in the outer task just as with any non-nested task. You can find more information from http://msdn.microsoft.com/en-us/library/vstudio/dd997417(v=vs.100).aspx. And the code maybe like the following.

       

      var outer = Task.Factory.StartNew(() =>
    
            {
    
                Console.WriteLine("Outer task executing.");
    
    
                var nested = Task<int>.Factory.StartNew(() =>
    
                {
    
                    Console.WriteLine("Nested task starting.");
    
                    throw new ArgumentException("dddddddddddd");
    
                    Console.WriteLine("Nested task completing.");
    
    
                }).ContinueWith((t) =>
    
                {
    
                    Console.WriteLine("I have observed a inner {0}",
    
                        t.Exception.InnerException.GetType().Name);
    
                }, TaskContinuationOptions.OnlyOnFaulted);
    
    
    
            }).ContinueWith((t) =>
    
                {
    
                    Console.WriteLine("I have observed a outer {0}",
    
                        t.Exception.InnerException.GetType().Name);
    
                },
    
           TaskContinuationOptions.OnlyOnFaulted);
    

    Hope useful.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place. &lt;br/&gt; Click &lt;a href=&quot;http://support.microsoft.com/common/survey.aspx?showpage=1&amp;scid=sw%3Ben%3B3559&amp;theme=tech&quot;&gt; HERE&lt;/a&gt; to participate the survey.

    Tuesday, July 15, 2014 7:48 AM
    Moderator
  • Thanks . I am also referring to same http://msdn.microsoft.com/en-us/library/vstudio/dd997417(v=vs.100).aspx. But this link has a table which shows that status of parent and also exception thrown is propogated to parent. This clearly means that if you wait parent for attachedtoparent task then you should observe exception and this is exactly the difference between nested task and child task. Wong, Please correct if I am wrong here.
    Friday, July 18, 2014 7:56 AM