.Net Async Task ограничивает вызовы / сек - PullRequest
0 голосов
/ 12 апреля 2019

Итак, меня только что попросили ограничить количество вызовов, которые мой асинхронный код .Net делает до 5 / сек для конкретного API, который мы вызываем.

Пример наших асинхронных вызовов

var mylist = new List<Task<ApplicantDetails>>();

foreach (JToken result in results)
{
    mylist.Add(getCandidateResumeAsync(result));
}
await Task.WhenAll(mylist);

и фактический вызов API insode getCandidateResumeAsync

var candiateAttachmentsResponse = await Client.GetAsync(model["candidate"]["links"]["self"] + "/attachments");

Мне нужно выполнять функцию getCandidateResumeAsync только 5 раз в секунду.

Любые мыслио том, как я могу пойти по этому поводу?

Ответы [ 3 ]

3 голосов
/ 12 апреля 2019

Я бы использовал семафор, чтобы отследить, сколько вызовов в API в настоящее время выполняется, а затем выпускал один при каждом завершении вызова.Примерно так:

public class Throttle
{
    private readonly TimeSpan perPeriod = TimeSpan.FromSeconds(1);
    private readonly SemaphoreSlim actionSemaphore = new SemaphoreSlim(5, 5);

    public async Task Queue(Func<Task> action, CancellationToken cancel)
    {
        await actionSemaphore.WaitAsync(cancel);
        try
        {
            await action();
        }
        finally
        {
            await Task.Delay(perPeriod, cancel).ContinueWith(_ => actionSemaphore.Release(1), cancel);
        }

    }
}

perPeriod будет установлен на 1 секунду, а actionSemaphore будет установлен на 5, что означает, что он позволит запускать 5 запросов одновременно.Вы бы назвали это следующим образом:

Throttle t = new Throttle();
t.Queue(SomeAction, CancellationToken.None);

Затем можно дождаться завершения всех задач.

0 голосов
/ 15 апреля 2019

Спасибо за все предложения, ребята.

Просто добавьте это на случай, если это поможет кому-то еще в будущем.

Вот то, что я придумал, и, кажется, работает нормально.

        int counter = 0;
        foreach (JToken result in results)
        {
            counter++;
            if ((counter % 5) == 0) Task.Delay(1000);
            mylist.Add(getCandidateResumeAsync(result));
        }
0 голосов
/ 12 апреля 2019

В этом случае вы можете использовать ограничение параллельных задач вместо ограничения задач в секунду.

  static async Task  Main(string[] args) {

      const ushort maxConcurrentTasks = 5;

      var resultBag = new ConcurrentBag<ApplicantDetails>();
      var queue = new ConcurrentQueue<JToken>(items);

      var tasks = Enumerable.Range(0, maxConcurrentTasks)
        .Select(_ => GetResultAsync(queue, resultBag));

      await Task.WhenAll(tasks);

      var result = resultBag.ToArray();
    }

    private static async Task GetResultAsync(ConcurrentQueue<JToken> queue, ConcurrentBag<ApplicantDetails> resultBag) {
      while (queue.TryDequeue(out var queueItem)) {
        var result = await getCandidateResumeAsync(queueItem);
        resultBag.Add(result);
      }
    }

Я знаю, что это не совсем то, что вы ищете. Но, возможно, этот подход также приемлем для вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...