是低于code捕捉从原来的,继续和孩子任务TPL例外?任务、继续、孩子、code

2023-09-03 17:03:36 作者:不良老子

我使用TPL和异步/等待建立异步API在Web客户端的顶部,我的应用程序。而少数地方(一般在那里我需要运行一堆异步任务,等待他们都在最后)的,是下面的code段。我只是想确保我得到它正确的,因为即使写异步code是与第三方物流和异步/计谋调试/故障排除仍是挑战(交互式调试和故障排除的问题,在比较容易客户现场) - 所以想要得到它的权利

I am using TPL and async/await to build async API on top of webclient for my applications. And few of the places (generally where I need to run bunch of async tasks and wait for all of them in the end), am following code snippet. I just want to make sure I get it correct as even though writing async code is relatively easy with TPL and async/await debugging/trouble shooting is still challenging (both interactive debugging and troubleshooting issues at customer site) - so want to get it right.

我的目标:能捕捉从原来的任务所产生的异常,持续的任务以及子任务,这样我可以处理它(如果我需要)。我不希望有任何的例外是杉木和遗忘。

My Goal: Able to capture the exception generated from the original task, continuation tasks as well as child tasks, so that I can handle it (if I need to). I don't want any exception to be fir and forgotten.

基本原理我用: 1,.NET框架确保异常将被连接到任务 2. try / catch块可以应用于异步/等待,得到同步code(参考幻觉/可读性:http://channel9.msdn.com/Events/TechDays/Techdays-2014-the-Netherlands/Async-programming-deep-dive, http://blogs.msdn.com/b/ericlippert/archive/2010/11/19/asynchrony-in-c-5-part-seven-exceptions.aspx, http://msdn.microsoft.com/en-us/library/dd537614.aspx 等)

Basic principals I used: 1. .net framework ensures the exception will be attached to task 2. Try/catch block can be applied to async/await to give illusion/readability of sync code (references: http://channel9.msdn.com/Events/TechDays/Techdays-2014-the-Netherlands/Async-programming-deep-dive, http://blogs.msdn.com/b/ericlippert/archive/2010/11/19/asynchrony-in-c-5-part-seven-exceptions.aspx, http://msdn.microsoft.com/en-us/library/dd537614.aspx etc.)

问题 我想获得批准的预期目标(即我可以捕捉从原来的,继续和子任务除外)已经实现,并没有任何改进,我可以做样本:

Question I would like to get ratification that the desired goal (that I can capture exception from original, continuation and child tasks) has been achieved, and is there any improvements I can do to the sample:

有关前,会有其中的组成任务(前,展开代理任务)人会不会在所有(waitforactivati​​on状态)被激活的情况下,这样可以为WaitAll可能只是等待任务的开始?我的理解是,这种情况下不应该发生,因为在后续任务总是执行,并返回已经使用wnwrap跟踪的代理任务。只要我按照所有层和API类似的模式,该模式应该捕捉链接的任务全部聚集例外。

For ex, will there be a case where one of the composed tasks (for ex, unwrapped proxy task) won't be activated at all (waitforactivation state), so that waitall may simply wait for the task to start? My understanding is that these cases should never happens as the continuation task always executes, and returns a task which has been tracked by proxy using wnwrap. As long as I follow similar pattern in all layers and apis, the pattern should capture all the aggregated exceptions in the chained tasks.

注意:基本上是在寻找类似避免虚拟任务的建议正在创建的后续任务,如果原来的工作状态是不是跑到完成,或使用附加到父,这样我可以等待只能在父母等。看到所有的可能性,这样我可以挑选最好的选择,因为这种模式很严重依赖我的应用程序的错误处理。

Note: essentially am looking for suggestions like avoiding dummy task am creating in continuation task if original task status is not ran to completion, or using attach to parent so that i can wait only on parent etc. to see all possibilities so that i can pick the best option as this pattern am heavily relying in my app for error handling.

static void SyncAPIMethod(string[] args)
        {
            try
            {
                List<Task> composedTasks = new List<Task>();
                //the underlying async method follow the same pattern
                //either they chain the async tasks or, uses async/await 
                //wherever possible as its easy to read and write the code
                var task = FooAsync();
                composedTasks.Add(task);
                var taskContinuation = task.ContinueWith(t =>
                    {
                        //Intentionally not using TaskContinuationOptions, so that the 
                        //continuation task always runs - so that i can capture exception
                        //in case something is wrong in the continuation
                        List<Task> childTasks = new List<Task>();
                        if (t.Status == TaskStatus.RanToCompletion)
                        {

                            for (int i = 1; i <= 5; i++)
                            {
                                var childTask = FooAsync();
                                childTasks.Add(childTask);
                            }

                        }
                        //in case of faulted, it just returns dummy task whose status is set to 
                        //'RanToCompletion'
                        Task wa = Task.WhenAll(childTasks);
                        return wa;
                    });
                composedTasks.Add(taskContinuation);
                //the unwrapped task should capture the 'aggregated' exception from childtasks
                var unwrappedProxyTask = taskContinuation.Unwrap();
                composedTasks.Add(unwrappedProxyTask);
                //waiting on all tasks, so the exception will be thrown if any of the tasks fail
                Task.WaitAll(composedTasks.ToArray());
            }
            catch (AggregateException ag)
            {
                foreach(Exception ex in ag.Flatten().InnerExceptions)
                {
                    Console.WriteLine(ex);
                    //handle it
                }
            }
        }

最好的问候。

Best Regards.

推荐答案

从注​​释:

IMO,这code可能本来可以更简单和优雅   与异步/等待。我不明白为什么你坚持的理由   ContinueWith和展开,以及为什么你同时添加了内,外   (展开)任务composedTasks。

IMO, this code could could have been much more simpler and elegant with async/await. I don't understand the reason why you stick with ContinueWith and Unwrap, and why you add both the inner and the outer (unwrapped) task to composedTasks.

我的意思是一样的东西下面。我认为它做同样的事情,你原来的code,但没有在 composedTasks ContinueWith 和展开。你几乎从来不需要这些,如果你使用异步/计谋

What I meant is something like below. I think it does the same thing as you original code, but without unnecessary redundancy in form of composedTasks, ContinueWith and Unwrap. You almost never need those if you use async/await.

static void Main(string[] args)
{
    Func<Task> doAsync = async () =>
    {
        await FooAsync().ConfigureAwait(false);

        List<Task> childTasks = new List<Task>();
        for (int i = 1; i <= 5; i++)
        {
            var childTask = FooAsync();
            childTasks.Add(childTask);
        }

        await Task.WhenAll(childTasks);
    };

    try
    {
        doAsync().Wait();
    }
    catch (AggregateException ag)
    {
        foreach (Exception ex in ag.Flatten().InnerExceptions)
        {
            Console.WriteLine(ex);
            //handle it
        }
    }
}

static async Task FooAsync()
{
    // simulate some CPU-bound work
    Thread.Sleep(1000); 
    // we could have avoided blocking like this:        
    // await Task.Run(() => Thread.Sleep(1000)).ConfigureAwait(false);

    // introduce asynchrony
    // FooAsync returns an incomplete Task to the caller here
    await Task.Delay(1000).ConfigureAwait(false);
}

更新以报告的评价:

有一些使用情况下,创建子任务后,我继续   调用更多的'独立'的任务。

there are some use cases where i continue after 'creating child tasks' to invoke more 'independent' tasks.

基本上有任何异步任务工作流三个常见的场景:顺序组合,并联组成,或这两个(混合组成)的任意组合:

Basically, there are three common scenarios for any asynchronous task workflow: sequential composition, parallel composition, or any combination of these two (mixed composition):

顺序组合: 青云卫1号少儿重疾险保险公司实力如何 这款少儿重疾险值得买吗

sequential composition:

await task1;
await task2;
await task3;

并联组成:

parallel composition:

await Task.WhenAll(task1, task2, task3);

// or

await Task.WhenAny(task1, task2, task3);

混合组成:

mixed composition:

var func4 = new Func<Task>(async () => { await task2; await task3; });
await Task.WhenAll(task1, func4());

如果上述任何任务在CPU密集型的工作,你可以使用 Task.Run 为,例如:

If any task of the above does a CPU-bound work, you can use Task.Run for that, e.g.:

    var task1 = Task.Run(() => CalcPi(numOfPiDigits));

其中, CalcPi 是一个同步的方法进行实际计算。

Where CalcPi is a synchronous method doing the actual calculation.