【问题标题】:Converting convenience methods that use Tasks转换使用任务的便捷方法
【发布时间】:2015-01-30 20:31:40
【问题描述】:

我经常编写具有方便方法的代码,这些方法基本上包装了其他方法。这是一个简单的例子:

public class WithoutAsync
{
    public static ReadOnlyCollection<Response> GetResponses(IEnumerable<Request> fromRequests)
    {
        var ret = new List<Response>();

        foreach (Request r in fromRequests)
        {
            ret.Add(new Response());
        }

        return ret.AsReadOnly();
    }

    //convenience method
    public static Response GetResponse(Request fromRequest)
    {
        return GetResponses(new Request[] {fromRequest})[0];
    }
}

现在我想 await 长时间运行的操作,但我不太清楚如何改进此方法以用于 TPL:

public class WithAsync
{
    public static async Task<ReadOnlyCollection<Response>> GetResponses(IEnumerable<Request> fromRequests)
    {
        var awaitableResponses = new List<Task<Response>>();

        foreach (Request r in fromRequests)
        {
            awaitableResponses.Add(Task.Run<Response>(async () =>
                {
                    await Task.Delay(10000); //simulate some long running async op.
                    return new Response();
                }));
        }

        return new List<Response>(await Task.WhenAll(awaitableResponses)).AsReadOnly();
    }

    //convenience method
    public static Task<Response> GetResponse(Request fromRequest)
    {
        return GetResponse(new Request[] { fromRequest });
    }
}

上面的便捷方法显然不起作用,因为它在真正需要返回Task&lt;Response&gt; 时尝试返回Task&lt;ReadOnlyCollection&lt;Response&gt;&gt;

这行得通:

//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
    return new Task<Response>(new Func<Response>(() => GetResponse(new Request[] { fromRequest }).Result[0]));
}

但它看起来真的很尴尬,更重要的是,它阻塞了.Result[0],它可能在 UI 线程上。

有什么好方法可以完成我想做的事情吗?

【问题讨论】:

  • 这里只是猜测:public static async Task&lt;Response&gt; GetResponseAsync(Request fromRequest) { var collection = await GetResponsesAsync(new Request[] { fromRequest }); return collection[0]; });,假设您相应地重命名了不方便方法的async 版本。我还没有测试过——它仍然会阻塞吗?
  • 如果你有Task.Run(async () =&gt; { await Task.Delay(10000); return new Response(); });,这里就不需要Task.Run。如果操作已经是async,则不需要在线程池线程中运行。

标签: c# .net task-parallel-library async-await convenience-methods


【解决方案1】:

你试图避免使用那种“方便的方法”async,但没有理由这样做。

你想要的是调用另一个方法,等到有响应,然后得到第一个也是唯一一个。您可以通过将其设为 async 并使用 await 来做到这一点:

async Task<Response> GetResponseAsync(Request fromRequest)
{
    var responses = await GetResponsesAsync(new[] { fromRequest });
    return responses.Single();
}

虽然在这种特定情况下更好的解决方案是切换并让单个 GetResponse 实际上完成单个请求的工作,并让多个 GetRsponses 调用它:

async Task<ReadOnlyCollection<Response>> GetResponsesAsync(IEnumerable<Request> fromRequests)
{
    return (await Task.WhenAll(fromRequests.Select(GetResponse))).ToList().AsReadOnly();
}

async Task<Response> GetResponseAsync(Request fromRequest)
{
    await Task.Delay(10000); //simulate some long running async op.
    return new Response();
}

注意事项:

  • 我知道这是一个示例,但可能没有理由使用 Task.Run 来代替简单的 async 调用。
  • 约定是使用“Async”后缀(即GetResponseAsync)命名async 方法。
  • 我还将返回集合的方法的名称复数化了。

【讨论】:

  • 这正是框架的工作方式。 OP 实现的主要问题是您所描述的需要切换的内容。需要注意的是,一旦完成,OP 可以根据需要从外部异步方法中提取一些代码到非异步“方便”方法中。这些方法的一个很好的例子是 Web API,它的 ExceptionLoggerExceptionHandler 类。
  • 很好的答案,谢谢。你能详细说明不使用Task.Run吗?不确定我是否跟随。另外,我知道“异步”后缀,但对何时合适感到困惑。 MS 似乎懒得使用它(例如 Task.Delay 而不是 Task.DelayAsync)。最后,多元化问题是我的一个错字:)
  • @roryap Task.Run 将工作卸载到ThreadPool。您似乎不需要与当前使用的线程不同的线程。 Task.Run 主要用于从“重要”线程(如 UI 线程)中卸载工作,或通过利用多个线程并行化 CPU 密集型工作。
  • 所以当您说“而不是简单的async 调用”时,您的意思是async lambda?那不是也卸载到ThreadPool吗?
  • @roryap 首先,async 方法和任何类型的 async 匿名委托(如 lambda 表达式)之间没有区别。不,async 方法在调用线程上同步运行,直到达到 await,然后将其余的注册为延续(只要没有 SynchronizationContext 告诉它,它将在 ThreadPool 上运行)。
【解决方案2】:

我仍然坚持使用I3arnon 的答案,因为它写得很好,内容丰富,但我想提交自己的答案,因为我意识到我几乎在那里.这是我一直在努力寻找的async 便捷方法:

//convenience method
public static async Task<Response> GetResponse(Request fromRequest)
{
    return (await GetResponses(new Request[] { fromRequest }))[0];
}

【讨论】:

    猜你喜欢
    • 2017-03-09
    • 2014-03-24
    • 2012-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 2021-08-09
    • 2015-07-01
    相关资源
    最近更新 更多