2012-10-03 100 views
2

異步方法時,我創建了使用Visual Studio 2012混淆行爲調用內部ASP.NET

一個ASP的WebApplication如果我修改默認的頁面如下:

public partial class _Default : Page 
{ 
    static async Task PerformSleepingTask() 
    { 
     Action action =() => 
     { 
      Thread.Sleep(TimeSpan.FromSeconds(0.5)); 
      int dummy = 3; // Just a nice place to put a break point 
     }; 
     await Task.Run(action); 
    } 


    protected void Page_Load(object sender, EventArgs e) 
    { 
     Task performSleepingTask = PerformSleepingTask(); 
     performSleepingTask.Wait(); 
    } 
} 

在調用performSleepingTask.Wait()它掛起無限期。


有趣的是,如果我設置在web.config:

<appSettings> 

    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" /> 
</appSettings> 

然後它的工作。 Wait函數等待睡眠在其他線程上完成,然後繼續。


有人可以解釋:

  • 爲什麼它掛?
  • 他們爲什麼有什麼叫TaskFriendlySynchronizationContext? (由於它會導致任務掛起,我不會把它稱爲「友好」)

  • 是否有一個「最佳實踐」爲調用從頁面處理方法async的方法呢?

這是我想出了其工作的實施,但感覺像笨拙代碼:

protected void Page_Load(object sender, EventArgs e) 
    { 
     ManualResetEvent mre = new ManualResetEvent(false); 
     Action act =() => 
     { 
      Task performSleepingTask = PerformSleepingTask(); 
      performSleepingTask.Wait(); 
      mre.Set(); 
     }; 
     act.BeginInvoke(null, null); 
     mre.WaitOne(TimeSpan.FromSeconds(1.0)); 
    } 
+0

我試圖弄清楚爲什麼你想讓一個線程睡在一個aspx頁面 –

+0

@勞倫斯約翰遜 - 這是一個故意設計的例子,爲了說明行爲。如果需要,您可以將其想象爲「PerformExpensiveDatabaseTasks」。 –

回答

4

爲什麼它掛?

的代表PerformSleepingTaskTask正試圖恢復後的await使PerformSleepingTask可以返回。它試圖重新輸入ASP.NET請求上下文,該請求上下文被Wait的調用阻止。 This causes a deadlock, as I expound on my blog

爲了避免僵局,遵循以下最佳實踐:

  1. 使用async一路下跌。不要阻止async的代碼。
  2. 在你的「庫」方法中使用ConfigureAwait(false)

爲什麼他們有一種稱爲TaskFriendlySynchronizationContext? (由於它會導致任務掛起,我不會把它稱爲「友好」)

TaskFriendlySynchronizationContext使用新AspNetSynchronizationContext(在.NET 4.0 TaskFriendlySynchronizationContext已更名LegacyTaskFriendlySynchronizationContext),並且它還採用了全新的,async知道管道。

我不是100%肯定這一個,但我嫌疑,之所以Page_Load作品遺留SyncCtx是舊管道並沒有把SyncCtx到位呢。我不確定它爲什麼會這樣做,但(除非Page.Asyncfalse)。

是否有從頁面處理程序方法調用異步方法的「最佳做法」?

Absolutely。您可以讓您的事件處理程序async void或使用RegisterAsyncTask(new PageAsyncTask(...));。第一種方法比較簡單,但第二種方法受到ASP.NET團隊的青睞。

+0

同樣,如果您有任何*參與您正在推薦的項目或您正在推送的鏈接(比如說您的博客),那麼您*必須*提供披露。我看到[183帖子](http://stackoverflow.com/search?q=user%3A263693+url%3A%22http%3A%2F%2Fnitoprograms.blogspot.com%2F*%22)應該有這個披露,而這一個肯定不會。不這樣做可能會導致進一步的主持人行動。此外,近20%的帖子會引用您的博客。請注意FAQ的部分內容,指出「如果您的帖子中有很多是用於宣傳的,這可能不是您的網站」。 – casperOne

+0

由於我每天只能進行5次修改,因此可能需要一段時間才能清理。請耐心等待。 –

+0

當然,但在構建*新*答案時請記住這一點。 – casperOne