如何将异步方法放在列表中并迭代调用它们?

时间:2019-01-16 00:43:59

标签: c# arraylist async-await task

最近,我想对服务呼叫列表进行健康检查。它们都是异步任务(例如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)
}

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

谢谢!

3 个答案:

答案 0 :(得分:3)

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

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<bool>,其余的将照此进行:

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();

答案 1 :(得分:1)

基于评论:

  

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

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

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};");
}

答案 2 :(得分:0)

我认为您只是在寻找类似这样的简单内容?

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

我建议您像这样将类型从Action更改为Func<Task>

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();
}