【问题标题】:Need help understanding background tasks in async/await calls in Azure Functions需要帮助了解 Azure Functions 中异步/等待调用中的后台任务
【发布时间】:2021-10-13 23:13:09
【问题描述】:

我一直在 Azure Functions 中使用 C# 创建服务。我已阅读有关如何最佳使用 async/await 的指南,但不了解它在 Azure 函数上下文中的价值。

在我的 Azure 函数中,我对外部 API 进行了 3 次调用。我尝试使用 async/await 来启动我的 API 调用。这个想法是前两个任务各自返回一个列表,将其连接起来,然后与第三个列表进行比较。

比较完成后,所有项目都被发送到存储容器队列,由另一个使用队列触发器的函数处理。

我的异步实现如下:

var firstListTask = GetResourceListAsync(1);
var secondListTask = GetResourceListAsync(2);
var thirdListTask = GetOtherResourceListAsync(1);

var completed = await Task.WhenAll(firstListTask, secondListTask);
var resultList = completed[0].Concat(completed[1]);

var compareList = await thirdListTask; 
// LINQ here to filter out resultList based on compareList

运行上述程序,我得到大约 38 秒的执行时间。 我对异步实现的理解是我启动了所有 3 个异步调用来获取我的列表。

  • 前两个任务通过 'await Task.WhenAll...' 等待 - 此时线程退出异步方法并“执行其他操作”,直到 API 返回有效负载
  • 接收到 API 有效负载,然后恢复该方法并继续执行下一条指令(连接两个列表)
  • 第三个任务随后使用 'await thirdListTask' 等待,它退出异步方法并“执行其他操作”,直到 API 返回有效负载
  • 接收到 API 有效负载,然后恢复该方法并继续执行下一条指令(过滤列表)

现在如果我同步运行相同的代码,我会得到大约 40 秒的执行时间:

var firstList = GetResourceList(1)
var secondList = GetResourceList(2);
var resultList = firstList.Concat(secondListTask)

var compaireList = GetOtherResourceList(1);
var finalList = // linq query to filter out resultList based on compareList

我可以看到异步函数的运行速度比同步函数快 2 秒,我假设这是因为 thirdListTaskfirstListTasksecondListTask 同时启动?

我对异步实现的问题是我不明白在 Azure Functions 的上下文中 “做其他事情” 意味着什么。据我了解,除了下一行的操作外,别无其他可做,但在有效负载从 API 返回之前,它无法在那里进行。

此外,以下代码示例是否与我的第一个异步实现做同样的事情?看到 Azure Functions 的示例对每个异步调用使用 await 只是为了等待下一行中的另一个调用,我感到非常困惑。

var firstList = await GetResourceListAsync(1);
var secondList = await GetResourceListAsync(2);

var resultList = firstList.Concat(secondList);

var compareList= await GetOtherResourceListAsync(1);
// LINQ here to filter out resultList based on compareList

我已经尝试阅读 MS 的 Azure Functions 最佳实践以及关于 stackoverflow 上的 async/await 的类似问题,但我似乎无法理解上述内容。谁能帮忙简化一下?

【问题讨论】:

  • 一个很好的问题,欢迎来到 Stack Overflow!

标签: c# azure async-await azure-functions


【解决方案1】:
var firstListTask = GetResourceListAsync(1);
var secondListTask = GetResourceListAsync(2);
var thirdListTask = GetOtherResourceListAsync(1);

这将启动所有 3 个任务。现在所有 3 个 API 调用都在运行。

var completed = await Task.WhenAll(firstListTask, secondListTask);

此异步等待直到两个任务完成。它释放了线程去“做其他事情”这是什么其他事情?无论框架需要它是什么。这是一个被释放的资源,因此它可以用于运行另一个异步操作的延续等。

var compareList = await thirdListTask; 

此时,您的 API 调用很可能已经完成,因为它是从其他 2 开始的。如果它完成了,await 将提取值或在任务出错时抛出异常。如果它仍在进行中,它将异步等待它完成,从而释放线程以“去做其他事情”

var firstList = await GetResourceListAsync(1);
var secondList = await GetResourceListAsync(2);

var resultList = firstList.Concat(secondList);

var compareList= await GetOtherResourceListAsync(1);

这与您的第一个示例不同。如果例如您的所有 API 调用需要 5 秒才能完成,总运行时间将为 15 秒,因为您按顺序启动并等待它完成。在您的第一个示例中,总运行时间大约为 5 秒,因此快了 3 倍。

【讨论】:

    猜你喜欢
    • 2019-08-27
    • 2018-02-23
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 2013-05-20
    • 2021-12-07
    相关资源
    最近更新 更多