【问题标题】:How to put async method in a list and invoke them iteratively?如何将异步方法放在列表中并迭代调用它们?
【发布时间】:2019-01-16 00:43:59
【问题描述】:

最近我想对服务调用列表实施健康检查。它们都是异步任务(例如Task<IHttpOperationResponse<XXX_Model>> method_name(...)

我想将它们全部放入一个列表中。我按照这篇文章的答案:Storing a list of methods in C# 但是,它们是异步方法。

我是这样说的: 异步方法的集合

List<Action> _functions = new List<Action> {
      () => accountDetailsServiceProvider.GetEmployer(EmployerId),                                  
      () => accountServiceProvider.GetAccountStatus(EmployerId)
}

有人可以指导我正确的方法来实现将异步方法放入列表并迭代调用它们吗?

提前致谢!

【问题讨论】:

  • 另外请不要粘贴代码图片。
  • 这看起来很奇怪,因为方法名称似乎表明它们返回一个值,但您没有对它们做任何事情。
  • 如果XXX_Model 中的Task&lt;IHttpOperationResponse&lt;XXX_Model&gt;&gt; 表示不同的类型,您将无法观察返回的响应。因为方法的集合应该具有相同的签名。
  • 请准确解释您要做什么,因为这似乎是 XYish 现在,过去您的代码而不是图像,以及包括签名在内的任何其他信息,不清楚您的要求
  • 对不起各位,我现在改成代码了。

标签: c# arraylist async-await task


【解决方案1】:

首先,您需要使您的方法异步。这意味着他们必须返回一个任务。例如:

public static async Task Foo()
{
    await Task.Delay(1);
    Console.WriteLine("Foo!");
}

public static async Task Bar()
{
    await Task.Delay(1);
    Console.WriteLine("Bar!");
}

然后要将它们放入列表中,您必须将列表定义为包含正确的类型。由于异步方法实际上返回了一些东西,所以它是一个 Func,而不是一个动作。它返回一个任务。

var actions = new List<Func<Task>>
{
    Foo, Bar
};

要调用它们,请在列表中选择(使用 Linq)来调用它们。这将创建一个任务列表来代替功能列表。

var tasks = actions.Select( x => x() );

那就等着他们吧:

await Task.WhenAll(tasks);

完整示例:

public static async Task MainAsync()
{
    var actions = new List<Func<Task>>
    {
        Foo, Bar
    };
    var tasks = actions.Select( x => x() );
    await Task.WhenAll(tasks);
}

输出:

Foo!
Bar!

Example on DotNetFiddle

如果你的方法返回一个布尔值,那么返回类型就变成Task&lt;bool&gt;,其余的也一样:

public static async Task<bool> Foo()
{
    await Task.Delay(1);
    Console.WriteLine("Foo!");
    return true;
}

public static async Task<bool> Bar()
{
    await Task.Delay(1);
    Console.WriteLine("Bar!");
    return true;
}

var actions = new List<Func<Task<bool>>>
{
    Foo, Bar
};

var tasks = actions.Select( x => x() );

await Task.WhenAll(tasks);

等待它们之后,您可以使用另外一条 LINQ 语句将任务转换为它们的结果:

List<bool> results = tasks.Select( task => task.Result ).ToList();

【讨论】:

  • 我真的很喜欢这个 Linq 解决方案。
  • 如果我想单独检查方法怎么办?我不使用WhenAll,我应该使用什么?
  • @Terence,你将无法检查你的方法的结果,只有方法完成与否。
  • 您当然可以根据需要单独等待任务(使用 await 关键字),或者如果您只想检查它是否已完成而不等待,您可以检查 IsCompleted 属性的任务。
  • @Fabio 你是对的。我只是想检查方法是否执行成功。
【解决方案2】:

基于 cmets:

但是,我的任务需要每个方法调用的布尔值, 因为我必须向前端报告状态是否 服务是否关闭

为将返回所需布尔值的方法创建一个包装方法

public async Task<Result> Check(string name, Func<Task> execute)
{
    try
    {
        await execute();
        return new Result(name, true, string.Empty);
    }
    catch (Exception ex)
    {
        return new Result(name, false, ex.Message);
    }
}

public class Result
{
    public string Name { get; }
    public bool Success { get; }
    public string Message { get; }
    public Result(string name, bool success, string message) 
        => (Name, Success, Message) = (name, success, message);
}

那么您不需要收集代表,而是收集Task

var tasks = new[]
{
    Check(nameof(details.GetEmployer), () => details.GetEmployer(Id)),
    Check(nameof(accounts.GetAccountStatus), () => accounts.GetAccountStatus(Id)),
};

var completed = await Task.WhenAll(tasks);

foreach (var task in completed)
{
    Console.WriteLine($"Task: {task.Name}, Success: {task.Success};");
}

【讨论】:

    【解决方案3】:

    我认为您只是在寻找像这样简单的东西?

    var myList = new List<Action>()
    {
        async() => { await Foo.GetBarAsync(); },
        ...
    };
    

    我建议您将类型从 Action 更改为 Func&lt;Task&gt;,就像这样。

    var myList = new List<Func<Task>>()
    {
      async() => { await Foo.GetBarAsync(); },
    };
    

    您可以在此处阅读有关原因的更多信息:https://blogs.msdn.microsoft.com/pfxteam/2012/02/08/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/

    调用(简化)

    foreach (var action in myList)
    {
        await action.Invoke();
    }
    

    【讨论】:

    • 感谢您的回答。还有一个问题:有什么方法可以迭代调用每个方法?
    • 应该注意,这会导致火灾和遗忘。基本上你不会知道这些方法中的任何一个何时完成。
    • 我想知道为什么投反对票?也许有人可以解释这怎么不能回答 OP 的问题?
    • @Svek 我认为您的回答很简单而且很好。不知道反对票的来源。
    猜你喜欢
    • 2012-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多