【问题标题】:How to execute all Tasks in the List<Task> regardless of exceptions无论异常如何执行 List<Task> 中的所有任务
【发布时间】:2016-03-27 01:37:47
【问题描述】:

我有如下所示的代码。在每个 Task 函数中,我将一个对对象的引用传递给可以记录任务中发生的任何异常的对象。我想要的是无论是否发生异常,我的列表中的每个任务都要执行(因为我已经在函数中记录了异常。)所以我想要的是 lobj_CustomFieldsTasks 任务列表中的所有任务要执行无论他们中的任何一个遇到异常。有什么建议?

注意:此代码无法复制和工作 - 它是我正在尝试做的事情的摘要。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace LANrevTargetBL
{
public class myEventArgs : EventArgs
{
    public eAPIFunction CompletedProcessName { get; set; }
    public List<Exception> AsyncErrors { get; set; }
}


/// <summary>
/// Class for Devices.  Used for Applying and Removing profiles 
/// </summary>
public class DevicesVM
{

    private async Task LoadCustomFieldsAndServerProfiles(myEventArgs pobj_myEventArgs)
    {
        List<Task> lobj_CustomFieldsTasks;
        lobj_CustomFieldsTasks = new List<Task>();

        // The iobj_AllDevices list is defined as a List<Device> where Device is a custom data class I have
        foreach (Device lobj_Device in iobj_AllDevices)
        {
            lobj_CustomFieldsTasks.Add(MakeCustomFieldCall(lobj_Device.ID, pobj_myEventArgs));
        }

        try
        {
            await Task.WhenAll(lobj_CustomFieldsTasks);
        }
        catch (Exception ex)
        {
            pobj_myEventArgs.AsyncErrors.Add(ex);
        }
    }

    private async Task MakeCustomFieldCall(int pi_DeviceID, myEventArgs pobj_myEventArgs1)
    {
        string ls_WorkRoomList;
        Device lobj_Device;
        APICallHelper lobj_APICallHelper;
        CustomFieldMetaData.RootObject lobj_CustomFieldResult;

        try
        {

            lobj_APICallHelper = new APICallHelper();

            var lobj_ReturnTask = await lobj_APICallHelper.MakeAPICall(eAPIFunction.eCustomFields, "", pi_DeviceID.ToString());

            if (lobj_ReturnTask.Exceptions == null)
            {
                if (iobj_AllDevices != null)
                {
                    lobj_Device = (from lobj_FoundItem in iobj_AllDevices
                                   where lobj_FoundItem.ID == pi_DeviceID
                                   select lobj_FoundItem).ElementAt(0);
                }
            }
            else
            {
                foreach (Exception lobj_Exception in lobj_ReturnTask.Exceptions)
                {
                    pobj_myEventArgs1.AsyncErrors.Add(lobj_Exception);
                }
            }

        }
        catch (Exception ex)
        {
            pobj_myEventArgs1.AsyncErrors.Add(ex);
        }
    }
}

}

【问题讨论】:

    标签: c# exception-handling async-await task-parallel-library


    【解决方案1】:

    使用普通的foreach 而不是Task.WhenAll

    foreach (var task in lobj_CustomFieldsTasks)
    {
        try { await task; }
        catch (Exception ex) { pobj_myEventArgs.AsyncErrors.Add(ex); }
    }
    

    上述工作是因为您已经提前创建(启动)了所有任务。 Task.WhenAll 只是 await 完成所有任务的便捷方式,带有您不想要的额外异常/取消逻辑。相比之下,Task.WhenAny 不能那么容易被替换,但没有必要这样做,因为它不会引发异常。

    更多详情请见await (C# Reference)

    【讨论】:

    • 这不会利用 WhenAll 实现的并行处理。
    • WhenAll 没有实现并行处理。一旦你启动了所有任务(并且你在第一个循环中完成了),它们已经并行运行,无论是否使用 WhenAnyWhenAllawait 顺序。
    【解决方案2】:

    那么WhenAll函数基本上已经做了你想做的事了。它运行所有任务以完成。如果一个或多个任务因异常而出现故障,则生成的Task 也将进入故障状态并显示AggregateException。一旦所有任务完成,await 就会抛出此异常。您可以在InnerExceptions 属性中获取您的任务中发生的真正异常。所以它基本上已经按照您尝试的方式收集了异常。

    总而言之:您不需要更改任何内容,除了记录您可能需要解包的异常。

    【讨论】:

      【解决方案3】:

      无需尝试/捕捉任何东西。 WaitAll 运行所有任务,并为您汇总任何异常。查看https://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx 的示例。

      在示例中,所有任务都已执行,并且当它们完成时会抛出 AggregateException。如果您对这些聚合异常不感兴趣,当然可以忽略此异常。

      【讨论】:

        【解决方案4】:

        你们都是对的。 WhenAll 将完全按照我的意愿行事。我的问题是我的异常处理程序中实际上抛出了一个异常。我的 AsyncErrors 对象实际上是空的,因此它看起来像 WhenAll 没有按需要处理异常。感谢大家的意见。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-02-07
          • 1970-01-01
          • 1970-01-01
          • 2018-10-18
          • 2011-11-22
          • 1970-01-01
          • 1970-01-01
          • 2013-09-28
          相关资源
          最近更新 更多