gpt4 book ai didi

c# - 我怎样才能等待一系列任务并停止等待第一个异常?

转载 作者:太空狗 更新时间:2023-10-29 23:04:55 25 4
gpt4 key购买 nike

我有一系列任务,我正在用 Task.WhenAll 等待它们.我的任务经常失败,在这种情况下,我会用一个消息框通知用户,以便她可以重试。我的问题是报告错误会延迟到所有任务完成。相反,我想在第一个任务抛出异常时立即通知用户。换句话说,我想要一个快速失败的 Task.WhenAll 版本。由于不存在这样的内置方法,我尝试自己制作,但我的实现并不像我想要的那样。这是我想出的:

public static async Task<TResult[]> WhenAllFailFast<TResult>(
params Task<TResult>[] tasks)
{
foreach (var task in tasks)
{
await task.ConfigureAwait(false);
}
return await Task.WhenAll(tasks).ConfigureAwait(false);
}

这通常比原生的 Task.WhenAll 抛出速度更快,但通常不够快。在任务 #1 完成之前,不会观察到故障任务 #2。我怎样才能改进它,让它尽快失效?


更新:关于取消,目前我没有要求,但可以说为了保持一致性,第一个取消的任务应该立即停止等待。在这种情况下,从 WhenAllFailFast 返回的组合任务应该具有 Status == TaskStatus.Canceled

澄清:取消场景是关于用户单击取消 按钮以停止完成任务。它不是关于在出现异常时自动取消未完成的任务。

最佳答案

您最好的选择是使用 TaskCompletionSource 构建您的 WhenAllFailFast 方法.您可以 .ContinueWith() 每个具有同步延续的输入任务,当任务以 Faulted 状态结束时(使用相同的异常对象)使 TCS 出错。

也许是这样的(未完全测试):

using System;
using System.Threading;
using System.Threading.Tasks;

namespace stackoverflow
{
class Program
{
static async Task Main(string[] args)
{

var cts = new CancellationTokenSource();
cts.Cancel();
var arr = await WhenAllFastFail(
Task.FromResult(42),
Task.Delay(2000).ContinueWith<int>(t => throw new Exception("ouch")),
Task.FromCanceled<int>(cts.Token));

Console.WriteLine("Hello World!");
}

public static Task<TResult[]> WhenAllFastFail<TResult>(params Task<TResult>[] tasks)
{
if (tasks is null || tasks.Length == 0) return Task.FromResult(Array.Empty<TResult>());

// defensive copy.
var defensive = tasks.Clone() as Task<TResult>[];

var tcs = new TaskCompletionSource<TResult[]>();
var remaining = defensive.Length;

Action<Task> check = t =>
{
switch (t.Status)
{
case TaskStatus.Faulted:
// we 'try' as some other task may beat us to the punch.
tcs.TrySetException(t.Exception.InnerException);
break;
case TaskStatus.Canceled:
// we 'try' as some other task may beat us to the punch.
tcs.TrySetCanceled();
break;
default:

// we can safely set here as no other task remains to run.
if (Interlocked.Decrement(ref remaining) == 0)
{
// get the results into an array.
var results = new TResult[defensive.Length];
for (var i = 0; i < tasks.Length; ++i) results[i] = defensive[i].Result;
tcs.SetResult(results);
}
break;
}
};

foreach (var task in defensive)
{
task.ContinueWith(check, default, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

return tcs.Task;
}
}
}

编辑:解包 AggregateException、取消支持、返回结果数组。防御数组突变、null 和空。显式 TaskScheduler。

关于c# - 我怎样才能等待一系列任务并停止等待第一个异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57313252/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com