У меня есть асинхронный метод, который использует ограниченный ресурс. Если ресурс становится недоступным, я хочу отложить будущие вызовы, пока ресурс не станет доступен снова.Как асинхронно задерживать все будущие вызовы при обработке исключения
В принципе, если при доступе к ограниченному ресурсу возникает исключение, один поток обрабатывает ошибку, задерживая все потоки, которые также попадают в исключение, и другие потоки, которые вызывают метод. После 5
секунд поток будет повторять доступ к ресурсу. Это похоже на дросселирование.
Я внедрил это злоупотреблениеTaskCompletionSource
в сочетании с SemaphoreSlim
. Кажется, что он работает. Может ли это быть улучшено, чтобы быть меньше ... храбрым?
// Use SemaphoreSlim to make sure only one thread handles an error at a time.
private static readonly SemaphoreSlim mySemaphore = new SemaphoreSlim(1);
// Use TaskCompletionSource as a flag to delay threads while an error is handled.
private static volatile TaskCompletionSource<bool> myFlag;
static MyClass()
{
myFlag = new TaskCompletionSource<bool>();
myFlag.SetResult(false); // At startup there is no error being handled.
}
public async Task DoSomethingAsync()
{
while (true)
{
await myFlag.Task; // Wait if an error is being handled.
try
{
await ... // Call some asynchronous operations here that can cause errors.
return;
}
catch
{
await mySemaphore.WaitAsync(); // Wait so only one thread handles an error.
bool wasHandled = await myFlag.Task; // Wait and check if error was handled.
if (wasHandled == false)
{
// Reset TaskCompletionSource so error handling on other threads waits.
myFlag = new TaskCompletionSource<bool>();
mySemaphore.Release();
await Task.Delay(5000); // "Handle" the error by waiting 5 seconds.
myFlag.SetResult(true); // Notify waiting threads an error was handled.
// Reset TaskCompletionSource
myFlag = new TaskCompletionSource<bool>();
myFlag.SetResult(false);
}
else // (wasHandled == true)
{
mySemaphore.Release(); // Move along, nothing to see here.
}
}
}
}
Чтобы пояснить, почему я считаю, что это должно быть улучшено: Я использую TaskCompletionSource
создать awaitable логического состояния, и сбросить его, я должен создавать новую TaskCompletionSource
каждый раз. Я не думаю, что это предназначено для TaskCompletionSource
.
Я просмотрел ManualResetEvent
и AutoResetEvent
, поскольку они, кажется, делают то, что мне нужно, но они не предлагают асинхронную функциональность.
Это ужасная идея –
@HristoYankov Вот почему я публикую здесь. Позаботьтесь о том, чтобы предложить лучшую идею? –
Для этого есть библиотеки, такие как Polly (https://github.com/App-vNext/Polly). Похоже, что их реализация Circuit Breaker может быть полезна для вашего сценария. –