下面的代码是否捕获了 TPL 中原始任务、延续任务和子任务的异常?

2024-01-02

我正在使用 TPL 和 async/await 在 Webclient 之上为我的应用程序构建异步 API。很少有地方(通常是我需要运行一堆异步任务并最终等待所有任务的地方)遵循代码片段。我只是想确保我得到它的正确性,即使使用 TPL 和 async/await 编写异步代码相对容易调试/故障排除仍然具有挑战性(交互式调试和客户现场故障排除问题) - 所以希望得到正确的结果。

My Goal:能够捕获原始任务、延续任务以及子任务生成的异常,以便我可以处理它(如果需要)。我不希望任何例外被忽视和遗忘。

我使用的基本原则:1. .net框架确保异常将附加到任务 2. Try/catch 块可以应用于 async/await 以提供同步代码的错觉/可读性(参考:http://channel9.msdn.com/Events/TechDays/Techdays-2014-the-Netherlands/Async-programming-deep-dive 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://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 http://msdn.microsoft.com/en-us/library/dd537614.aspx etc.)

Question:我希望得到批准,表明预期目标(我可以捕获原始任务、延续任务和子任务中的异常)已经实现,并且我可以对示例进行任何改进:

例如,是否会出现其中一个组合任务(例如,未包装的代理任务)根本不会被激活(waitforactivation 状态)的情况,因此 waitall 可能只是等待任务启动?我的理解是,这些情况永远不会发生,因为延续任务始终执行,并返回已使用 wnwrap 代理跟踪的任务。只要我在所有层和 API 中遵循类似的模式,该模式就应该捕获链式任务中的所有聚合异常。

Note:本质上是在寻找建议,例如如果原始任务状态未运行到完成,则避免在继续任务中创建虚拟任务,或者使用附加到父级,以便我只能等待父级等查看所有可能性,以便我可以选择最好的选项,因为此模式严重依赖我的应用程序进行错误处理。

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
                }
            }
        }

来自评论:

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

我的意思是像下面这样。我认为它与原始代码做同样的事情,但没有不必要的冗余composedTasks, ContinueWith and Unwrap。如果你使用的话你几乎永远不需要这些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);
}

Updated解决评论:

在某些用例中,我在“创建子任务”后继续 调用更多“独立”任务。

基本上,任何异步任务工作流都存在三种常见场景:顺序组合、并行组合或这两者的任意组合(混合组合):

  • 顺序组合:

    await task1;
    await task2;
    await task3;
    
  • 平行组合:

    await Task.WhenAll(task1, task2, task3);
    
    // or
    
    await Task.WhenAny(task1, task2, task3);
    
  • 混合成分:

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

如果上述任何任务执行 CPU 密集型工作,您可以使用Task.Run为此,例如:

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

Where CalcPi是进行实际计算的同步方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

下面的代码是否捕获了 TPL 中原始任务、延续任务和子任务的异常? 的相关文章

随机推荐

  • Databricks 更改默认目录

    似乎当我连接到 Databricks Warehouse 时 它 使用的是默认目录hive metastore 有没有办法将统一目录定义为默认目录 我知道我可以运行查询 USE CATALOG MAIN 然后当前会话将使用unity cat
  • 通过引用返回 std::vector 导致分段错误[重复]

    这个问题在这里已经有答案了 我有一个创建非常大的 std vector 的函数 返回此向量时 由于其大小 我不想再次复制它 因此 我想返回对该向量的引用 然而 这样做会给我带来分段错误 为什么是这样 解决办法是什么 这是我的代码 std v
  • C DLL 无法在 C# 中加载

    我有一个简单的 C DLL 不是 COM 我还有一个运行它的 C 2 0 应用程序 dllimport 该dll被放置在应用程序的exe中 我知道该 dll 没问题 而且我的应用程序调用它成功读取了它 但是当我将它们移动到另一台计算机时 它
  • 强制显式删除 Java 对象

    我正在开发一个处理大量非常密集流量的 Java 服务器 服务器接受来自客户端的数据包 通常为数兆字节 并将它们转发给其他客户端 服务器从不显式存储任何传入 传出数据包 然而服务器不断遇到OutOfMemoryException例外情况 I
  • 操作完成工作后执行函数

    大家好 今天开始使用 Redux 因此正在构建一个非常基本的项目 通过单击 递增 递减 按钮来递增 递减数字 这是一个小代码 请看一下 操作 创建文件 export const increment num gt return type in
  • fullcalendar:如何添加每天所有事件的总持续时间

    我注意到这个问题被问了几次 但没有实际的正确答案或良好的反馈来引导正确的道路 我在用全日历 https fullcalendar io javascript 插件并尝试添加每天多个事件的总小时数 然后我将在每天的页眉或页脚中显示总和 我尝试
  • 如何从react-native-fbsdk获取用户信息(电子邮件、姓名等)?

    当用户通过 Facebook 进行身份验证时 我尝试访问用户的电子邮件和姓名以进行设置和帐户 我已经准备好了react native fbsdk的文档 但我没有在任何地方看到它 接受的答案使用fetch但是 SDK 也能够执行此请求 因此
  • 您没有权限访问该页面,请咨​​询您的系统管理员

    我是管理员 并且我已经安装了来自以下位置的 News Letter 扩展 当我尝试使用它时 它返回以下错误 错误 您没有权限访问此页面 请咨 询您的系统管理员 登录管理员 进入System gt User Group 编辑您的管理员用户组
  • OpenCV 向量到 Mat 但不是 element->row

    有一种非常简单的方法可以从向量构造 Mat 只需执行以下操作 vector
  • 单独向 firestore 文档添加字段

    以下代码创建一个 firestore 集合并向其中添加数据 function saveID sender psid complete let data new Object data ID sender psid data TASK com
  • 如何从字符串中提取主题标签?

    我需要从接收字符串的函数中提取 这是我所做的 def hashtag str lst for i in str split if i 0 lst append i 1 return lst 我的代码确实有效 但它会分割单词 因此对于示例字符
  • Linq 到 Xml 到 Datagridview

    好吧 这里开始发疯了 我有以下代码 var query from c in db Descendants Customer select c Elements dgvEditCusts DataSource query ToList 在此
  • “file_exists”问题中的特殊字符(php)

    我使用特殊字符 瑞典字母 现在 我有一些文件夹 其中包含分类图像 文件夹按类别命名 for i 1 i lt 5 i if file exists big images i jpg echo Inne unlink big images i
  • vcruntime140_app.dll 未包含在 Microsoft Visual C++ 2017 Redistributable (x64) 中?

    我用QT开发了一个GUI 我的发行版本需要vcruntime140 app dll才能运行 我在 SysWOW64 文件夹中找不到此 dll 因此我重新安装了 Microsoft Visual Stuio C 2017 Redistribu
  • 消息中至少有一个安全令牌无法验证

    服务器配置
  • 使用 Assembly.GetCallingAssembly() 不会返回调用程序集

    在我的 ASP NET MVC 应用程序中 我使用一个小助手来迭代所有控制器 该助手位于与我的 MVC 应用程序不同的程序集中 我正在引用它 问题是 当在助手中调用 Assembly GetCallingAssembly 方法时 它不会返回
  • 强制清理会话cookie(firefox、chrome)

    某些浏览器 Firefox Chrome 在设计上不会在您关闭会话 cookie 时清除会话 cookie 如果您设置了某种记住我的开关 例如在 FF 中 转到 选项 gt 常规 gt Firefox 启动时 gt 显示我的窗口 和上次的选
  • 如何防止移动端视图加载到webview中

    正在设计一个带有 webview 的 android 应用程序 但总是在网站的移动视图加载时 但我想加载桌面版本 您可以尝试以下操作 WebView getSettings setUserAgentString Mozilla 5 0 Wi
  • Python修改错误列表?

    我正在尝试使用以下命令生成素数列表this http en wikipedia org wiki Sieve of Eratosthenes方法 我需要循环遍历每个数字 2 n 并检查它是否是 2 n 的倍数 由于某种原因 错误的列表似乎被
  • 下面的代码是否捕获了 TPL 中原始任务、延续任务和子任务的异常?

    我正在使用 TPL 和 async await 在 Webclient 之上为我的应用程序构建异步 API 很少有地方 通常是我需要运行一堆异步任务并最终等待所有任务的地方 遵循代码片段 我只是想确保我得到它的正确性 即使使用 TPL 和