Тупик с асинхронным/ожиданием

Предположим, я пишу собственный фильтр MVC, который выполняет некоторые асинхронные вызовы в переопределениях метода, например:

public class MyActionFilter : System.Web.Mvc.ActionFilterAttribute
{
  public override void OnActionExecuted(ActionExecutedContext ctx)
  {
    var stuff = ConfigureAwaitHelper1().Result;
    // do stuff
  }

  public override void OnActionExecuting(ActionExecutingContext ctx)
  {
    var stuff = ConfigureAwaitHelper2().Result;
    // do stuff
  }

  private async Task<string> ConfigureAwaitHelper1()
  {
    var result = await client.GetAsStringAsync("blah.com").ConfigureAwait(false);
    return result;
  }

  private async Task<string> ConfigureAwaitHelper2()
  {
    return await client.GetAsStringAsync("blah.com").ConfigureAwait(false);
  }
}

Почему OnActionExecuting блокируется, а OnActionExecuted нет? Принципиальной разницы между ними не вижу. Акт возврата происходит только после завершения асинхронной задачи, что скорее похоже на помещение результата в локальную переменную «анонимного возврата» перед его возвратом, поэтому я не понимаю, почему первый должен зайти в тупик.


person gzak    schedule 18.07.2016    source источник
comment
Вы спрашиваете, когда я нарушаю правила одним способом, это не работает, но когда я нарушаю правила другим способом, это работает, почему мне разрешено нарушать правила вторым способом, ответ: вам не разрешено нарушать правила 2-й способ, вам просто повезло, что вас не поймали на нарушении правил. Я не знаю, почему второй работает, но для меня это не имеет значения, потому что вы все равно не должны его блокировать.   -  person Scott Chamberlain    schedule 19.07.2016
comment
Кроме того, для того, чтобы .ConfigureAwait(false) фактически позволял вам выполнять .Result без взаимоблокировки, каждое ожидание вниз по цепочке вызовов должно иметь установленное .ConfigureAwait(false). Если один из них этого не делает, и именно это ожидание вызвало продолжение, вы попадете в тупик. Вам нужно показать код для client.GetAsStringAsync и каждого вызова async/await под ним, пока вы не найдете тот, в котором все еще есть данные контекста.   -  person Scott Chamberlain    schedule 19.07.2016
comment
Какая альтернатива у меня есть? ActionFilterAttribute является синхронным, и я не могу контролировать client. Я понимаю, что вы беспокоитесь о том, чтобы сделать это полностью, но давайте просто предположим, что этот образец завершен и что внутри client.GetAsStringAsync() больше нечего настраивать.   -  person gzak    schedule 19.07.2016
comment
Можете ли вы перепроверить, что это OnActionExecuting блокировка, а не OnActionExecuted?   -  person Diego    schedule 19.07.2016
comment
Я только что подтвердил, что оба тупика на моей машине, но у моей коллеги были некоторые дополнительные изменения, когда она удалила ключевые слова async/await из метода client.GetAsStringAsync() и просто вернула необработанные задачи напрямую. В ее случае она увидела поведение, описанное здесь.   -  person gzak    schedule 19.07.2016
comment
Возможный дубликат Async Await Handler Deadlock   -  person Igor    schedule 19.07.2016


Ответы (1)


Почему OnActionExecuting блокируется, а OnActionExecuted — нет?

Я удивлен, что это вообще работает. Причина, по которой вы столкнулись с тупиком, связана с тем, что вы вызываете .Result для Task. Это зло, и вам следует вызывать .Result и .Wait только в консольных приложениях.

person David Pine    schedule 19.07.2016