在C#中,我正在调用一个公共API,其API限制为每秒10次调用

Asa*_*han 8 c# multithreading task task-parallel-library

在C#中,我正在调用一个公共API,其API限制为每秒10次调用.API有多种方法,不同的用户可以一次调用不同的方法,因此可能会出现"速率限制达到"异常.

我有以下类结构:

public class MyServiceManager
{
    public int Method1()
    {
    }

    public void Method2()
    {
    }

    public string Method3()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

多个用户可以一次调用不同的方法,如何维护静态调用队列或任务,以便我可以监视所有请求并在一秒钟内只接受10个请求

Sir*_*ufo 9

您可以基于构建TaskLimiter SemaphoreSlim

public class TaskLimiter
{
    private readonly TimeSpan _timespan;
    private readonly SemaphoreSlim _semaphore;

    public TaskLimiter(int count, TimeSpan timespan)
    {
        _semaphore = new SemaphoreSlim(count, count);
        _timespan = timespan;
    }

    public async Task LimitAsync(Func<Task> taskFactory)
    {
        await _semaphore.WaitAsync().ConfigureAwait(false);
        var task = taskFactory();
        task.ContinueWith(async e =>
        {
            await Task.Delay(_timespan);
            _semaphore.Release(1);
        });
        await task;
    }

    public async Task<T> LimitAsync<T>(Func<Task<T>> taskFactory)
    {
        await _semaphore.WaitAsync().ConfigureAwait(false);
        var task = taskFactory();
        task.ContinueWith(async e =>
        {
            await Task.Delay(_timespan);
            _semaphore.Release(1);
        });
        return await task;
    }
}
Run Code Online (Sandbox Code Playgroud)

它会

  • 等待信号量"槽"
  • 开始真正的任务
  • 在真实任务完成后,在给定的时间跨度之后释放信号量槽

这是一个示例用法

public class Program
{
    public static void Main()
    {
        RunAsync().Wait();
    }

    public static async Task RunAsync()
    {
        var limiter = new TaskLimiter(10, TimeSpan.FromSeconds(1));

        // create 100 tasks 
        var tasks = Enumerable.Range(1, 100)
           .Select(e => limiter.LimitAsync(() => DoSomeActionAsync(e)));
        // wait unitl all 100 tasks are completed
        await Task.WhenAll(tasks).ConfigureAwait(false);
    }

    static readonly Random _rng = new Random();

    public static async Task DoSomeActionAsync(int i)
    {
        await Task.Delay(150 + _rng.Next(150)).ConfigureAwait(false);
        Console.WriteLine("Completed Action {0}", i);
    }

}
Run Code Online (Sandbox Code Playgroud)