Ждать любого асинхронного метода и (событие или логическое значение)
У меня есть этот код:
ManualResetEvent EventListenerStopped;
...
while (true)
{
IAsyncResult iar = this.ListenerHttp.BeginGetContext(ProcessRequest, null);
if (WaitHandle.WaitAny(new[] { this.EventListenerStopped, iar.AsyncWaitHandle }) == 0)
return;
}
В основном это ожидает любого из двух событий:
- если запрос получен, он обрабатывает его и ждет следующего.
- если вызывается EventListenerStopped, он выходит из цикла.
Этот код прекрасно работает в производстве уже довольно давно.
Я хотел попробовать преобразовать его в новый механизм await/async и, похоже, не могу найти хороший простой способ сделать это.
Я попытался с логическим значением, вызывающий может превратить в ложь. Очевидно, он не работает, поскольку выходит из цикла только после получения и обработки нового запроса:
bool RunLoop;
...
while (this.RunLoop)
{
HttpListenerContext listenerContext = await this.ListenerHttp.GetContextAsync();
ProcessRequest(listenerContext);
}
Мне интересно, можно ли вообще переписать мой простой цикл в старом стиле с помощью async / await. Если да, кто-нибудь захочет показать мне, как?
2 ответа
Это не относится к async-await, но вы, вероятно, ищете CancellationToken
(который в любом случае используется с большим количеством асинхронно-ожидаемого кода):
http://blogs.msdn.com/b/pfxteam/archive/2009/05/22/9635790.aspx
Пример кода "BlockingOperation" кажется похожим на то, что вы пытаетесь сделать:
void BlockingOperation(CancellationToken token) { ManualResetEvent mre = new ManualResetEvent(false); //register a callback that will set the MRE CancellationTokenRegistration registration = token.Register(() => mre.Set()); using (registration) { mre.WaitOne(); if (token.IsCancellationRequested) //did cancellation wake us? throw new OperationCanceledException(token); } //dispose the registration, which performs the deregisteration. }
Ну, во-первых, я должен указать, что старый код не совсем корректен. При работе с Begin
/ End
шаблон, вы всегда должны звонить End
, даже если вы хотите (или сделали) отменить операцию. End
часто используется для распоряжения ресурсами.
Если вы хотите использовать отмену, CancellationToken
Вероятно, лучший подход:
while (true)
{
// Throws an OperationCanceledException when cancellationToken is canceled.
var request = await this.ListenerHttp.GetContextAsync(cancellationToken);
ProcessRequest(request);
}
Есть альтернативы - можно сделать что-то вроде Task.WhenAny
и есть даже реализации AsyncManualResetEvent
Таким образом, можно создать почти построчный эквивалент старого кода, но IMO подход с использованием маркеров отмены был бы чище.
Например, используя AsyncManualResetEvent
из моей библиотеки AsyncEx:
AsyncManualResetEvent eventListenerStopped;
while (true)
{
var task = GetContextAndProcessRequestAsync();
if (await Task.WhenAny(eventListenerStopped.WaitAsync(), task) != task)
return;
}
async Task GetContextAndProcessRequestAsync()
{
var request = await this.ListenerHttp.GetContextAsync();
ProcessRequest(request);
}
Но лично я бы поменял на использование CancellationToken
,