如何在C#中设置一组异步任务,并使用源代码处理错误

如何在C#中设置一组异步任务,并使用源代码处理错误,c#,async-await,task-parallel-library,C#,Async Await,Task Parallel Library,如果我有一组方法要调用,并且希望异步调用。我可以这样做: var status = new DiagnosticsResult(); try { var taskList = new List<Task> { _someService.Method1(), _someService.Method2(), _someService.Method3(), _someService.Method4(),

如果我有一组方法要调用,并且希望异步调用。我可以这样做:

var status = new DiagnosticsResult();

try
{
    var taskList = new List<Task>
    {
        _someService.Method1(),
        _someService.Method2(),
        _someService.Method3(),
        _someService.Method4(),
        _someService.Method5(),
        _someService.Method6(),
        _someService.Method7(),
        _someService.Method8()
    };

    Task.WaitAll(taskList.ToArray());
}
catch (AggregateException ex)
{
    foreach (var innerException in ex.InnerExceptions)
    {
        // Do Something
    }
}
var status=新诊断结果();
尝试
{
var taskList=新列表
{
_someService.Method1(),
_someService.Method2(),
_someService.Method3(),
_someService.Method4(),
_someService.Method5(),
_someService.Method6(),
_someService.Method7(),
_someService.Method8()
};
Task.WaitAll(taskList.ToArray());
}
捕获(聚合异常)
{
foreach(ex.InnerExceptions中的var innerException)
{
//做点什么
}
}

如果我只是想知道发生了一个错误,但是,如果我需要知道哪些方法引发异常,哪些方法成功了呢?

如果您将任务列表保留在范围内,您可以在
捕获中迭代这些任务,或者在
等待
之后查找
任务的异常。异常

您可以使用
aggregateeexception.Handle
Func
来过滤消息、类型等。此外,如果您在
Aggregate
上迭代,您应该调用
flatte
来排列所有异常

编辑


我面临的问题是,一旦有人抛出其余的更改,它就会变为取消状态。我需要它们全部运行并通过或失败,然后我需要确定结果。我将任务列表移到了try之外,是的,它被更新了,但是当4次失败时,我得到了3次“RanToCompletion”,1次“Faulted”,其余的“Cancelled”

这种方法似乎是等待每个人,并单独处理他们的异常,而不是集体处理。两种方法如下所示:

public async Task Test1() {
    var taskList = new List<Task>() {
         _someService.Method1.ContinueWith(tsk => {
             //handle ex
         }).Unwrap()
    }
    /*
     * or..
     * */
    try {
        await _someService.Method1
    }catch (AggregateException ex) {
        /***
         * Handle ex
         * *//
    }               
}
公共异步任务Test1(){
var taskList=新列表(){
_someService.Method1.ContinueWith(tsk=>{
//手柄
}).Unwrap()
}
/*
*或者。。
* */
试一试{
等待服务。方法1
}捕获(聚合异常){
/***
*手柄
* *//
}               
}

一种可能的方法是在每个任务中传递一个令牌,并在评估结果时启用关联:

public static Task<WhenAllWithTokenResult<TToken>> WhenAllWithToken<TToken>(Tuple<Task, TToken>[] tasks)
{
    var successfulTasks = new List<Tuple<Task, TToken>>();
    var failedTasks = new List<Tuple<Task, TToken>>();
    var cancelledTasks = new List<Tuple<Task, TToken>>();
    int amountCompleted = 0;
    var taskCompletionSource = new TaskCompletionSource<WhenAllWithTokenResult<TToken>>();
    // Register ContinueWith callback for each task and add it to the according result list when completed
    foreach (var tuple in tasks)
    {
        var copyOfTuple = tuple;
        tuple.Item1.ContinueWith(_ =>
        {               
            if (_.IsFaulted)
            {
                failedTasks.Add(copyOfTuple);
            }
            else if (_.IsCanceled)
            {
                cancelledTasks.Add(copyOfTuple);
            }
            else if (_.IsCompleted)
            {
                successfulTasks.Add(copyOfTuple);
            }
            if (Interlocked.Increment(ref amountCompleted) == tasks.Length)
            {
                // All tasks finished so let's set the result of this method
                taskCompletionSource.SetResult(new WhenAllWithTokenResult<TToken>(successfulTasks, failedTasks, cancelledTasks));
            }
        });
    }
    return taskCompletionSource.Task;
}

public class WhenAllWithTokenResult<TToken>
{
    public IList<Tuple<Task, TToken>> SuccessfulTasks { get; private set; }
    public IList<Tuple<Task, TToken>> FailedTasks { get; private set; }
    public IList<Tuple<Task, TToken>> CancelledTasks { get; private set; }

    public WhenAllWithTokenResult(IList<Tuple<Task, TToken>> successfulTasks, IList<Tuple<Task, TToken>> failedTasks, IList<Tuple<Task, TToken>> cancelledTasks)
    {
        CancelledTasks = cancelledTasks;
        SuccessfulTasks = successfulTasks;
        FailedTasks = failedTasks;
    }
}
公共静态任务whallwithtoken(元组[]任务)
{
var successfulTasks=新列表();
var failedTasks=新列表();
var canceledtasks=新列表();
int amountCompleted=0;
var taskCompletionSource=新的taskCompletionSource();
//为每个任务注册ContinueWith callback,并在完成后将其添加到相应的结果列表中
foreach(任务中的变量元组)
{
var copyOfTuple=元组;
tuple.Item1.ContinueWith(=>
{               
如果(u.IsFaulted)
{
失败的任务。添加(复制次数);
}
否则,如果(u.iscancelled)
{
已取消的任务。添加(复制次数);
}
否则,如果(u.IsCompleted)
{
成功的任务。添加(复制次数);
}
if(联锁增量(参考数量完成)=任务长度)
{
//所有任务都已完成,因此让我们设置此方法的结果
taskCompletionSource.SetResult(新WhallWithTokenResult(成功的任务、失败的任务、取消的任务));
}
});
}
返回taskCompletionSource.Task;
}
使用TokenResult时的公共类
{
公共IList成功任务{get;private set;}
公共IList失败任务{get;private set;}
公共IList取消任务{get;private set;}
public whallwithTokenResult(IList successfulTasks、IList failedTasks、IList CanceledTasks)
{
取消任务=取消任务;
SuccessfulTasks=成功的任务;
FailedTasks=FailedTasks;
}
}
并且像这样使用它:

WhenAllWithTokenResult<string> result = 
    await WhenAllWithToken(
                    new[]
                    {
                        Tuple.Create(_someService.Method1(), "Method1"),
                        Tuple.Create(_someService.Method2(), "Method2"),
                        Tuple.Create(_someService.Method3(), "Method3"),
                        Tuple.Create(_someService.Method4(), "Method4")
                    });

foreach (var item in result.SuccessfulTasks)
{
    Console.WriteLine("Successful: {0}", item.Item2);
}
foreach (var item in result.FailedTasks)
{
    Console.WriteLine("Failed: {0}", item.Item2);
}
foreach (var item in result.CancelledTasks)
{
    Console.WriteLine("Cancelled: {0}", item.Item2);
}
whallWithTokenResult结果=
用代币等待(
新[]
{
Tuple.Create(_someService.Method1(),“Method1”),
Tuple.Create(_someService.Method2(),“Method2”),
Tuple.Create(_someService.Method3(),“Method3”),
Tuple.Create(_someService.Method4(),“Method4”)
});
foreach(result.SuccessfulTasks中的var项)
{
WriteLine(“成功:{0}”,item.Item2);
}
foreach(result.FailedTasks中的var项)
{
WriteLine(“失败:{0}”,item.Item2);
}
foreach(结果中的var项。已取消任务)
{
WriteLine(“取消:{0}”,item.Item2);
}

请注意1)令牌是一个通用的,只是为了允许关联而传递。所以,它可以是你想要的任何东西。2) WhenAllWithToken与WhenAll具有不同的语义:它返回一个任务,该任务在所有输入任务成功、失败或取消时完成,但不是在之前完成。

是否可以对每个异常使用堆栈跟踪?这应该会为您显示导致异常的每个错误的方法。如果我使用您的代码,我可以使用
innerException.TargetSite.Name
和一些字符串操作来获取方法名称(如果您正在查找的话)。对我来说,方法名Method1有一个TargetSite.name of
b_uuu2_0
我面临的问题是,一旦有人将其余更改抛出到取消状态。我需要它们全部运行并通过或失败,然后我需要确定结果。我将任务列表移到了try之外,是的,它被更新了,但是当4失败时,我得到了3个“RanToCompletion”,1个“Faulted”,其余的“Cancelled”。只是想一想,您是否检查了
的行为,而所有的
可能都是相同的,但值得检查。否则,
waiting
每一个都会给出相同的结果,只会在
连续中捕获异常
等待
它们的
展开
。。。不是很优雅,但很有效。一次运行一个等待是我所拥有的