это хорошая идея использовать блокировку для этой цели?Имеет ли он какие-либо негативные последствия, и, как я ожидаю, это приведет к появлению очередей?
Блокировка не является хорошим решением.Плохие эффекты заключаются в том, что он блокирует поток пула потоков с момента, когда работа находится в очереди , до времени, когда работа завершается.Поэтому, если ваш код помещает в очередь 1000 запросов, он будет вызывать Task.Run
1000 раз и потенциально использовать это количество потоков пула потоков, каждый из которых ничего не делает, кроме ожидания блокировки.
Кроме того, блокировкине строго FIFO.Они просто в основном-сорта-FIFO.Это потому, что строго-FIFO блокировки вызывают другие проблемы, такие как конвои блокировки; ссылки в этом выпуске обсуждают "справедливость" блокировки (т. Е. Поведение FIFO) .
Итак, я рекомендую реальную очередь.Вы можете использовать ActionBlock<T>
из TPL Dataflow , чтобы действовать как истинная очередь.Так как ваши запросы имеют результаты , вы можете использовать TaskCompletionSource<T>
для кода постановки в очередь, чтобы иметь возможность получить результат.TaskCompletionSource<T>
- это «асинхронный сигнал» - в этом случае мы используем его для уведомления вызывающего кода о том, что их конкретный запрос прошел через очередь и был выполнен.
private ActionBlock<TaskCompletionSource<int>> queue =
new ActionBlock<TaskCompletionSource<int>>(tcs =>
{
try { tcs.TrySetResult(MyTask()); }
catch (Exception ex) { tcs.TrySetException(ex); }
});
Каждый раз, когда мы отправляем TaskCompletionSource<T>
на этот queue
, он запускается MyTask()
и фиксирует результаты (будь то успешные или исключительные) и передает эти результаты в TaskCompletionSource<T>
.
Затем мы можем использовать его так:
public Task<int> DoMyTask() {
var tcs = new TaskCompletionSource<int>();
queue.Post(tcs);
return tcs.Task;
}
public void CallMyTask() {
var result = await DoMyTask();
}