c# - 在调用之间暂停的多个异步调用

标签 c# async-await

我有一个 IEnumerable<Task> ,其中每个任务将调用相同的端点。但是,端点每秒只能处理这么多调用。 如何在每次调用之间设置半秒延迟?

我已经尝试添加 Task.Delay(),但当然等待它们只是意味着应用在一次发送所有调用之前等待半秒。

这是一个代码片段:

    var resultTasks = orders
        .Select(async task => 
        {
            var result = new VendorTaskResult();
            try
            {
                result.Response = await result.CallVendorAsync();
            }
            catch(Exception ex)
            {
                result.Exception = ex;
            }
            return result;
        } );

    var results = Task.WhenAll(resultTasks);

我觉得我应该做这样的事情

    Task.WhenAll(resultTasks.EmitOverTime(500));

...但我该怎么做呢?

最佳答案

换句话说,您在问题中描述的是 rate limiting .您希望对您的客户端应用速率限制策略,因为您使用的 API 在服务器上强制执行此类策略以保护自己免受滥用。

虽然您可以自己实现速率限制,但我建议您使用一些完善的解决方案。 Rate Limiter from Davis Desmaisons是我随机挑选的那个,我立刻喜欢上了它。它具有可靠的文档、出色的覆盖范围并且易于使用。它也可以作为 NuGet package 获得。 .

查看下面的简单代码段,它演示了按顺序运行半重叠任务,同时在前一个任务开始后将任务延迟半秒开始。每个任务至少持续 750 毫秒。

using ComposableAsync;
using RateLimiter;
using System;
using System.Threading.Tasks;

namespace RateLimiterTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Log("Starting tasks ...");
            var constraint = TimeLimiter.GetFromMaxCountByInterval(1, TimeSpan.FromSeconds(0.5));
            var tasks = new[]
            {
                DoWorkAsync("Task1", constraint),
                DoWorkAsync("Task2", constraint),
                DoWorkAsync("Task3", constraint),
                DoWorkAsync("Task4", constraint)
            };
            Task.WaitAll(tasks);
            Log("All tasks finished.");
            Console.ReadLine();
        }

        static void Log(string message)
        {
            Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff ") + message);
        }

        static async Task DoWorkAsync(string name, IDispatcher constraint)
        {
            await constraint;
            Log(name + " started");
            await Task.Delay(750);
            Log(name + " finished");
        }
    }
}

示例输出:

10:03:27.121 Starting tasks ...
10:03:27.154 Task1 started
10:03:27.658 Task2 started
10:03:27.911 Task1 finished
10:03:28.160 Task3 started
10:03:28.410 Task2 finished
10:03:28.680 Task4 started
10:03:28.913 Task3 finished
10:03:29.443 Task4 finished
10:03:29.443 All tasks finished.

如果您更改约束以允许每秒最多执行两个任务 (var constraint = TimeLimiter.GetFromMaxCountByInterval(2, TimeSpan.FromSeconds(1));),这与每秒执行一个任务不同半秒,然后输出可能是这样的:

10:06:03.237 Starting tasks ...
10:06:03.264 Task1 started
10:06:03.268 Task2 started
10:06:04.026 Task2 finished
10:06:04.031 Task1 finished
10:06:04.275 Task3 started
10:06:04.276 Task4 started
10:06:05.032 Task4 finished
10:06:05.032 Task3 finished
10:06:05.033 All tasks finished.

请注意,当前版本的 Rate Limiter 以 .NETFramework 4.7.2+ 或 .NETStandard 2.0+ 为目标。

关于c# - 在调用之间暂停的多个异步调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56835195/

相关文章:

c# - 有没有办法强制 Sitecore CMS 用户在第一次登录时更改密码?

c# - 将异常或消息从线程/任务传递到主线程

c# - 从同步服务层边界调用异步方法

javascript - 我需要一个 Promise 中的 Promise 及其 .then 先运行

c# - 向 MediatR 行为管道添加验证?

c# - 如何在 C# 中将所有消息存储在 Azure 存储队列上而不出列

c# - 根据多个条件外部连接两个数据表

python - 使用 asyncio 和 aiohttp 的多个非阻塞任务

c# - 为什么这段代码是同步运行的?

javascript - 从 JavaScript 中的异步函数返回值?