繁体   English   中英

如何将附加上下文信息与任务一起传递给 Task.WhenAll?

[英]How to pass additional context information along with tasks to Task.WhenAll?

考虑以下理想代码(不起作用)。 我基本上是在尝试创建一个返回特定对象的Task列表,将它们与string标识符相关联,然后使用Task.WhenAll批量执行所有这些。 在执行结束时,我需要让这些Task结果仍然与它们最初创建时使用的字符串标识符相关联:

public async Task<SomeObject> DoSomethingAsync(string thing)
{
    // implementation elided
}

public async Task<SomeObject> DoSomethingElseAsync(string thing)
{
    // different implementation elided
}

public async Task<IEnumerable<(string, SomeObject)>>
    DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
    var tasks = new List<(string, Task<SomeObject>)>();

    foreach (var item in someListOfStrings)
    {
        if (condition)
        {
            tasks.Add((item, DoSomethingAsync(item)));
        }
        else
        {
            tasks.Add((item, DoSomethingElseAsync(item)));
        }
    }

    // this doesn't compile, i'm just demonstrating what i want to achieve
    var results = await Task.WhenAll(tasks);

    return results;
}

这可以重写为以下内容:

public async Task<(string, SomeObject)> DoSomethingWrapperAsync(string thing)
    => (thing, await DoSomethingAsync(thing));

public async Task<(string, SomeObject)> DoSomethingElseWrapperAsync(string thing)
    => (thing, await DoSomethingElseAsync(thing));

public async Task<IEnumerable<(string, SomeObject)>>
    DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
    var tasks = new List<Task<(string, SomeObject)>>();

    foreach (var thing in someListOfStrings)
    {
        if (condition)
        {
            tasks.Add(DoSomethingWrapperAsync(thing));
        }
        else
        {
            tasks.Add(DoSomethingElseWrapperAsync(thing));
        }
    }

    // this does compile
    var results = await Task.WhenAll(tasks);

    return results;
}

问题是我需要一个额外的包装器方法来处理我将要调用的每个可能的离散异步函数,这感觉不必要和浪费并且代码很多(因为会有很多这些方法)。 有没有更简单的方法来实现我的需要?

我研究了实现 awaitable/awaiter 模式,但看不到如何让它与Task.WhenAll一起工作,它需要TaskTask<TResult>的集合,因为指导似乎是“不要扩展那些类”

您可以随时进行压缩:

public async Task<IEnumerable<(string, SomeObject)>>
    DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
  var tasks = someListOfStrings
      .Select(async item =>
          condition ?
          (item, await DoSomethingAsync(item)) :
          (item, await DoSomethingElseAsync(item)))
      .ToList();
  return await Task.WhenAll(tasks);
}

或者,您可以将输入作为单独的集合保存,稍后再压缩:

public async Task<IEnumerable<(string, SomeObject)>>
    DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
  // Reify input so we don't enumerate twice.
  var input = someListOfStrings.ToList();

  var tasks = input
      .Select(item =>
          condition ?
          DoSomethingAsync(item) :
          DoSomethingElseAsync(item))
      .ToList();
  var taskResults = await Task.WhenAll(tasks);

  return input.Zip(taskResults, (item, taskResult) => ((item, taskResult)));
}

据我所知,您正在使用condition来确定正在调用哪个方法(具有完全相同的签名)。 为什么不传递为每个项目调用的回调,而不是在foreach循环中执行逻辑?

public async Task<SomeObject> DoSomethingAsync(string thing)
{
    // ...
}

public async Task<SomeObject> DoSomethingElseAsync(string thing)
{
    // ...
}

public async Task<IEnumerable<(string, SomeObject)>> DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
    Func<string, Task<SomeObject>> callback = condition ? DoSomethingAsync : DoSomethingElseAsync;

    var results = await Task.WhenAll(
        someListOfStrings.Select(async thing => (thing, await callback(thing)))
    );
    return results;
}

此外,您可以将其提取为扩展方法。

public static class AsyncExtensions
{
    public static async Task<IEnumerable<(T, TResult)>> WhenAllAsync(this IEnumerable<T> collection, Func<T, Task<TResult>> selector)
    {
        var results = await Task.WhenAll(
            collection.Select(async item => (item, await selector(item)))
        );
        return results;
    }
}

public async Task MyMethodAsync()
{
    // ...

    var results = await myListOfStrings.WhenAllAsync(condition ? DoSomethingAsync : DoSomethingElseAsync);

    // ...
}

或者在回调中做映射。

public static class AsyncExtensions
{
    public static Task<IEnumerable<TResult>> WhenAllAsync(this IEnumerable<T> collection, Func<T, Task<TResult>> selector)
        => Task.WhenAll(collection.Select(selector)));
}

public async Task MyMethodAsync()
{
    // ...

    var results = await myListOfStrings.WhenAllAsync(async thing => (thing, await (condition ? DoSomethingAsync(thing) : DoSomethingElseAsync(thing))));

    // ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM