[英]How to execute multiple tasks in .NET Core, and check which finished successfully
I am trying to perform the following scenario:我正在尝试执行以下场景:
public class Controller : ControllerBase
{
private readonly List<string> _names = new List<string>()
{
"name1",
"name2"
};
[HttpGet]
public async Task<ActionResult> Get()
{
// Leaving this processing because it is the same in the original code, maybe there is something here that is relevant
var tasks = _names.ToDictionary(name => name, name => ExecuteRequest(name, async (value) =>
{
return await ReturnOrThrow(value);
}));
// I want to wait until all were finished
await Task.WhenAll(tasks.Values); // This already throws an exception, i don't want it to
// I don't want to catch exception here and just ignore it
var namesThatSucceeded = tasks.Count(t => t.Value.IsCompletedSuccessfully);
var namesThatThrewException = tasks.Count(t => t.Value.IsFaulted);
return Ok(new
{
Succeeded = namesThatSucceeded,
Failed = namesThatThrewException
});
}
// The "generic task structure" that runs the request, catches exception if thrown, and re-throws it.
private async Task<string> ExecuteRequest(string name, Func<string, Task<string>> request)
{
try
{
return await request(name);
}
catch (HttpRequestException e)
{
Console.WriteLine(e.Message);
throw; // I would prefer to just return Faulted Task here so it won't throw exception
}
}
// The actual processing
private async Task<string> ReturnOrThrow(string name)
{
if (name == "name1")
{
throw new HttpRequestException();
}
return await Task.FromResult(name);
}
}
You can wrap action with a high order function which has await and exception handler.您可以使用具有等待和异常处理程序的高阶函数来包装动作。
class Program
{
static async Task Main(string[] args)
{
var itemsToProcess = new[] { "one", "two" };
var results = itemsToProcess.ToDictionary(x => x, async (item) =>
{
try
{
var result = await DoAsync();
return ((Exception)null, result);
}
catch (Exception ex)
{
return (ex, (object)null);
}
});
await Task.WhenAll(results.Values);
foreach(var item in results)
{
Console.WriteLine(item.Key + (await item.Value).Item1 != null ? " Failed" : "Succeed");
}
}
public static async Task<object> DoAsync()
{
await Task.Delay(10);
throw new InvalidOperationException();
}
}
您是否在调用 ExecuteRequest 的 ToDictionary 中缺少等待?
async name => await ExecuteRequest
You cannot avoid exception throwing in awaiting for Task.WhenAll().您无法避免在等待 Task.WhenAll() 时抛出异常。 That's behavior by design.
这是设计行为。 But you can pass Task's status via it's Result value.
但是您可以通过它的 Result 值传递 Task 的状态。 Just extend the Result from string to (string Result, bool Success) and return false Success from catch without re-throwing Exception.
只需将 Result 从字符串扩展到 (string Result, bool Success) 并从 catch 返回 false Success 而不重新抛出异常。
public class Controller : ControllerBase
{
private readonly List<string> _names = new List<string>()
{
"name1",
"name2"
};
[HttpGet]
public async Task<ActionResult> Get()
{
// Leaving this processing because it is the same in the original code, maybe there is something here that is relevant
var tasks = _names.ToDictionary(name => name, name => ExecuteRequest(name, async (value) =>
{
return await ReturnOrThrow(value);
}));
await Task.WhenAll(tasks.Values); // Doesn't throw exception anymore, but you can access Success status from Task's Result tuple
var namesThatSucceeded = tasks.Count(t => t.Value.Result.Success);
var namesThatThrewException = tasks.Count(t => !t.Value.Result.Success);
return Ok(new
{
Succeeded = namesThatSucceeded,
Failed = namesThatThrewException
});
}
// The "generic task structure" that runs the request, catches exception if thrown, and re-throws it.
private async Task<(string Result, bool Success)> ExecuteRequest(string name, Func<string, Task<string>> request)
{
try
{
return (await request(name), true);
}
catch (HttpRequestException e)
{
Console.WriteLine(e.Message);
return (null, false);
}
}
// The actual processing
private async Task<string> ReturnOrThrow(string name)
{
if (name == "name1")
{
throw new HttpRequestException();
}
return await Task.FromResult(name);
}
}
You're welcome.别客气。
An easy way to suppress the exception of an awaited task is to pass the task as a single argument to Task.WhenAny
:抑制等待任务异常的一种简单方法是将任务作为单个参数传递给
Task.WhenAny
:
Creates a task that will complete when any of the supplied tasks have completed.
创建一个将在任何提供的任务完成后完成的任务。
await Task.WhenAny(Task.WhenAll(tasks.Values)); // Ignores the exception
This works because the task returned from Task.WhenAny
never fails.这是有效的,因为从
Task.WhenAny
返回的任务永远不会失败。 When it completes, it is always completed successfully.当它完成时,它总是成功完成。 There are two small drawbacks though:
但是有两个小缺点:
params Task[]
argument, so calling it results in an object allocation (avoid using it in hot paths).params Task[]
参数,因此调用它会导致对象分配(避免在热路径中使用它)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.