2013-12-16 148 views
2

我想了解與MVC異步/ AWAIT機制。現在我不考慮使用情況下我會出去的「正常流動」的(使用完全同步或異步滿端至端)。我只想確保瞭解爲什麼它不起作用。爲什麼我不能運行異步代碼同步

當調用「SyncMethod」時,它會無限期地掛起並永不返回。

當「AsyncMethod」被稱爲很快返回一個視圖沒有掛。

當調用「TaskMethod」時,它會很快返回一個視圖而不會掛起。

我不完全知道爲什麼當同步方法調用異步方法時,不可能返回結果。

我在這裏錯過了什麼?

using System; 
using System.Threading.Tasks; 
using System.Web.Mvc; 

namespace MvcAsyncAwaitTest.Controllers 
{ 
    public class HomeController : Controller 
    { 
     /// <summary> 
     /// Synchronous method running async method as sync 
     /// Hangs at Hello().Result, never returns 
     /// </summary> 
     /// <returns></returns> 
     public ActionResult SyncMethod() 
     { 
      ViewBag.Message = Hello().Result; 
      return View(); 
     } 

     /// <summary> 
     /// Asynchronous method awaiting asynchronous method 
     /// Do not hang 
     /// </summary> 
     /// <returns></returns> 
     public async Task<ActionResult> AsyncMethod() 
     { 
      ViewBag.Message = await Hello(); 
      return View("Index"); 
     } 

     /// <summary> 
     /// Synchronous method running a task based method synchronously 
     /// Returns a valid result 
     /// </summary> 
     /// <returns></returns> 
     public ActionResult TaskMethod() 
     { 
      ViewBag.Message = Hello2().Result; 

      return View("index"); 
     } 

     private async Task<string> Hello() 
     { 
      return await HelloImpl(); 
     } 

     private Task<string> Hello2() 
     { 
      return Task.Run(() => "Hello world 2"); 
     } 

     private async Task<String> HelloImpl() 
     { 
      return await Task.Run(() => "Hello World"); 
     } 
    } 
} 
+1

Stephen Cleary在此解釋了異步/死鎖困境:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – dcastro

+0

「Hello()」有什麼意義?它什麼都不做。只需直接調用'HelloImpl'即可。另外,爲了讓你可以等待你返回的任務,沒有必要做一個'async'方法。只是讓該方法不是'async'並直接返回該任務。 – Servy

+0

@Servy我可以做一個複雜的實現和所有的,但我者優先把一個很簡單的例子就明白了什麼是這裏的問題。但是如果你想要一個更加複雜的實現,就像使用完全一樣的asp.net標識框架。 – Erick

回答

0

這是一個僵局。在SyncMethod中,您正在等待自己:

Hello().Result等待您的請求的線程。

await HelloImpl()await Task.Run(...)裏面的HelloImpl()都返回執行到您的請求的線程,但不能因爲.Result阻止它。

6

問題的關鍵在於await將(默認情況下)捕獲當前的「上下文」並使用該方法恢復方法。在ASP.NET中,「背景」是一個SynchronizationContext,只允許一次一個線程英寸

因此,當您通過調用Result來阻止請求線程時,您將阻止該SynchronizationContext中的線程,因此Hello方法無法在該請求上下文中恢復。

您的Hello2().Result工作的原因是它實際上不是async方法;它只是將一些工作放在一個線程池線程上,這將獨立於請求線程完成。

我有一個blog entry進入全部細節。