2013-02-21 94 views
6

我有一個ASP.NET Web API控制器,我會認爲會異步操作。控制器設計爲第一次請求睡眠20秒,但立即爲後續請求提供服務。所以,我的預期時間表將是這樣的:爲什麼我的異步ASP.NET Web API控制器阻塞主線程?

  1. 培養要求1.
  2. 培養要求2
  3. 培養要求3
  4. 請求2個回報。
  5. 索取3個退貨。
  6. 等待~20秒。
  7. 申請1件退貨。

而是,請求返回,直到請求1完成。

我可以確認(基於調試輸出),入口線程和睏倦線程ID是不同的。我有意使用​​強制睡眠到一個單獨的線程,但應用程序仍然拒絕服務任何新的請求,直到該睡眠完成。

我錯過了關於異步Web API控制器真正工作的基礎知識嗎?


public class ValuesController : ApiController 
{ 
    private static bool _firstTime = true; 

    public async Task<string> Get() 
    { 
     Debug.WriteLine("Entry thread id: {0}. Sync: {1}", 
      Thread.CurrentThread.ManagedThreadId, 
      SynchronizationContext.Current); 
     await LongWaitAsync(); 
     return "FOOBAR"; 
    } 

    private Task LongWaitAsync() 
    { 
     return Task.Factory.StartNew(() => 
      { 
       if (_firstTime) 
       { 
        _firstTime = false; 
        Debug.WriteLine("Sleepy thread id: {0}. Sync: {1}", 
         Thread.CurrentThread.ManagedThreadId, 
         SynchronizationContext.Current); 
        Thread.Sleep(20000); 
        Debug.WriteLine("Finished sleeping"); 
       } 
      }, 
      CancellationToken.None, 
      TaskCreationOptions.LongRunning, 
      TaskScheduler.Default); 
    } 
} 
+0

作爲第一步,我會將_firstTime標記爲volatile,以確保單獨的線程可以看到它一致地改變。 – 2013-02-21 07:17:50

+0

你用什麼網絡服務器來託管? – 2013-02-21 07:17:50

+0

將'_firstTime'標記爲'volatile'沒有區別。我已經嘗試在Win 7 Pro上的IIS Express 8.0和IIS 7.5中進行託管。 – Snixtor 2013-02-21 07:20:20

回答

12

這實際上無關與服務器,以及一切與客戶端。 Chrome和Firefox似乎都不希望發送他們認爲「重複」請求的內容,直到第一個請求得到回覆。任一瀏覽器的單獨「私人」會話將立即從第二個請求返回。 Internet Explorer 9似乎沒有表現出這種行爲。

爲了從客戶端實現中分離出來,我將以下客戶端放在一起。

class Program 
{ 
    static void Main(string[] args) 
    { 
     var t1 = Task.Run(() => FetchData(1)); 
     var t2 = Task.Run(() => FetchData(2)); 
     var t3 = Task.Run(() => FetchData(3)); 

     var index = Task.WaitAny(t1, t2, t3); 
     Console.WriteLine("Task {0} finished first", index + 1); 

     Task.WaitAll(t1, t2, t3); 
     Console.WriteLine("All tasks have finished"); 

     Console.WriteLine("Press any key"); 
     Console.ReadKey(true); 
    } 

    static void FetchData(int clientNumber) 
    { 
     var client = new WebClient(); 
     string data = client.DownloadString("http://localhost:61852/api/values"); 
     Console.WriteLine("Client {0} got data: {1}", clientNumber, data); 
    } 
} 

它的輸出爲:

  1. 客戶端2獲得的數據: 「FOOBAR」
  2. 任務2完成第一
  3. 客戶端3得到的數據(啓動的幾毫秒內) 「FOOBAR」
  4. (長在這裏等)
  5. 客戶端1獲得的數據: 「FOOBAR」
  6. 全部•問完
+0

以爲我瘋了。感謝你的啓示! – NovaJoe 2014-10-01 14:19:29

0

在我的情況在這裏是輸出(它開關到另一個線程從5〜10):

Entry thread id: 5. Sync: System.Web.LegacyAspNetSynchronizationContext 
Sleepy thread id: 10. Sync: 
Finished sleeping 
The thread '<No Name>' (0x2c84) has exited with code 0 (0x0). 
Entry thread id: 7. Sync: System.Web.LegacyAspNetSynchronizationContext 
The thread '<No Name>' (0x1818) has exited with code 0 (0x0). 
Entry thread id: 5. Sync: System.Web.LegacyAspNetSynchronizationContext 
The thread '<No Name>' (0xd4c) has exited with code 0 (0x0). 
The thread '<No Name>' (0x2c30) has exited with code 0 (0x0). 

這可能是由於環境和機上運行(單核),其使運行時決定如何運行它。