Cancel child tasks when one of them throws an exce

2019-08-28 21:03发布

问题:

I'd like to improve the following code to add cancellation support. Basically, what I need to do is cancel all children as well as the parent task once a child throws an exception. I wrote the below code as a learning experience. I can see AggregateException only after all children finish, but I don't want that.

    static int GetSum()
    {
        var parent = Task<int>.Factory.StartNew(() =>
        {
            var children = new Task<int>[100];
            for (var i = 0; i < children.Length; i++)
            {
                var index = i;
                children[index] = Task<int>.Factory.StartNew(() =>
                {
                    var randomNumber = new Random().Next(5);
                    if (randomNumber == 0)
                    {
                        throw new Exception();
                    }

                    return randomNumber;
                }, TaskCreationOptions.AttachedToParent);
            }

            Task.WaitAll();
            Console.WriteLine("Children finished");
            return children.Sum(t => t.Result);
        });

        parent.Wait();
        Console.WriteLine("Parent finished");
        return parent.Result;
    }

I believe I need to use the following though I don't know how:

var source = new CancellationTokenSource();
var token = source.Token;

回答1:

you can just use Task.WaitAny instead of WaitAll and make a cancel request to the token once an AgregateException was thrown something like this

static int GetSum()
    {
        var tokenSource = new CancellationTokenSource();
        var token = tokenSource.Token;
        var parent = Task<int>.Factory.StartNew(() =>
        {
            var children = new Task<int>[100];
            for (var i = 0; i < children.Length; i++)
            {
                var index = i;
                children[index] = Task<int>.Factory.StartNew(() =>
                {
                    for (int j = 0; j < 100000; j++)
                    {


                    if (!token.IsCancellationRequested)
                    {


                        var randomNumber = new Random().Next(5);
                        if (randomNumber == 0)
                        {
                            throw new Exception();
                        }

                        return randomNumber;
                    }
                    else
                    {
                        token.ThrowIfCancellationRequested();
                    }
                    }
                    return 0;
                }
                , token);
            }
            try
            {
                Task.WaitAny(children);
            }
            catch (AggregateException ae)
            {
                tokenSource.Cancel();
                ae.Handle((task) =>
                    {
                        Console.WriteLine("Cancel all others child tasks  requested ");
                        return true;
                    });
            }

            Console.WriteLine("Children finished");
            return children.Sum(t => t.Result);
        });

        try
        {
            parent.Wait();
        }
        catch (AggregateException aex)
        {
            aex.Handle((task) =>
            {
                Console.WriteLine("Cancel child work  done ");
                return true;
            });              
        }

        Console.WriteLine("Parent finished");
        return parent.Result;
    }