Ждать любого асинхронного метода и (событие или логическое значение)

У меня есть этот код:

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,

Другие вопросы по тегам