2016-11-25 3 views
1

У меня есть событие щелчка на сервере на веб-сайте ASP.NET WebForms. В этом случае я вызываю метод, который, в свою очередь, вызывает его метод асинхронного партнера, добавляя .Wait() к вызову.Разница между вызовом .Wait() для метода async и Task.Run(). Wait()

Этот метод затем проходит несколько уровней вниз (т.е. , вызывает другой метод асинхронного, который вызывает другой метод асинхронного, и так далее) и в конечном итоге вызывает метод асинхронного на качестве HttpClient объекта. В этот момент нить, кажется, исчезает по кроличьей дыре; метод никогда не перезванивает.

Теперь я знаю, что асинхронная серия вызовов работает так, как ожидалось, потому что тот же код также вызывается из контроллера веб-API (метод контроллера вызывает асинхронную версию этого первого метода, а не синхронный «партнерский» метод) , который полностью асинхронный, и он возвращается, как ожидалось.

Так в основном у меня есть что-то вроде этого, который никогда не возвращает

protected void btn_Click(object sender, EventArgs e) 
    > Class1.DoSomething() 
     > Class1.DoSomethingAsync.Wait() 
      ... 
       > await ClassN.Authenticate() 
        { 
        await myHttpClient.PostAsync() // never returns 
        } 

Я попытался с помощью .ConfigureAwait(false) на этом первом методе асинхронным, но без какого-либо успеха.

У меня также есть это, что делает возвращение

Task<IHttpActionResult> MyWebApiMethod() 
    > await Class1.DoSomethingAsync() 
     ... 
      > await ClassN.Authenticate() 
       { 
       await myHttpClient.PostAsync() // does return 
       } 

Я обнаружил, что я могу сделать первую версию работы, если я могу изменить его на следующее:

protected void btn_Click(object sender, EventArgs e) 
    > Class1.DoSomething() 
     > Task.Run(async() => await Class1.DoSomethingAsync()).Wait() 
      ... 
       > await ClassN.Authenticate() 
        { 
        await myHttpClient.PostAsync() 
        } 

Но я не знаю почему.

Может кто-нибудь объяснить разницу между вызовом

Class1.DoSomethingAsync.Wait() 

и вызова

Task.Run(async() => await Class1.DoSomethingAsync()).Wait() 
+3

Почему бы вам не использовать его правильным способом. 'async void btn_Click' и' await Class1.DoSomethingAsync() '? – user3185569

+1

Не используйте 'Task.Run' с уже асинхронными методами, это отходы потоков. Просто измените подпись обработчика событий 'button_click', поскольку @ user3185569 предложил – Fabio

+0

@ user3185569 - Я не использовал это, потому что я не знал, что я использую' async' на событиях WebForms на стороне сервера. Спасибо, что указали на это. – awj

ответ

5

я объяснить это поведение в моем блоге Don't Block on Asynchronous Code и моей статье MSDN по asynchronous best practices.

нить, кажется, исчезает по кроличьей дыре; метод никогда не перезванивает.

Это происходит потому, что один из await с пытается возобновить на контексте ASP.NET, но поток запроса (с помощью этого контекста ASP.NET) блокируется ждет задача, чтобы закончить. Вот что заставляет зайти в тупик.

Я попытался использовать .ConfigureAwait (false) для этого первого метода асинхронизации, но без каких-либо успехов.

Для того чтобы избежать этого тупика, используя ConfigureAwait(false), он должен быть применен к каждыйawait в каждый метод вызывается. Так DoSomethingAsync должны использовать его для каждого await, каждый метод, который DoSomethingAsync вызовов должны использовать его для каждого await (например, Authenticate), каждого метода, что эти методы вызова должны использовать его для каждого await (например, PostAsync) и т.д.Обратите внимание, что в конце здесь вы зависите от кода библиотеки, и на самом деле HttpClient пропустил несколько из них в прошлом.

Я обнаружил, что могу сделать первую версию работы, если я ее сменил [использовать Task.Run].

Yup. Task.Run выполнит свой делегат в потоке пула потоков без любого контекста. Вот почему нет тупика: ни одна из await s не пытается возобновить работу в контексте ASP.NET.

Почему бы вам не использовать его правильным образом. async void btn_Click и ждет Class1.DoSomethingAsync()?

Не используйте Task.Run с уже асинхронными методами, это отходы потоков. Просто измените подпись button_click EventHandler

И вот право ответ: не блокировать асинхронного кода. Просто используйте обработчик события async void и вместо этого используйте await.

P.S. Ядро ASP.NET больше не имеет контекста ASP.NET, поэтому вы можете блокировать столько, сколько хотите, не опасаясь тупиков. Но вы все равно не должны, конечно, потому, что это неэффективно.

Смежные вопросы