不叫WebClient的異步回調在GET請求,我跑(類似):在ASP.NET MVC
public ActionResult Index(void) {
webClient.DownloadStringComplete += onComplete;
webClient.DownloadStringAsync(...);
return null;
}
我看到onComplete
不會被調用,直到後Index()
執行結束。 我可以看到onComplete
在一個不同的線程上被調用,其中Index
被執行。
問題:爲什麼會發生這種情況?爲什麼在請求處理線程完成之前,webClient的異步線程顯然被阻塞?
有沒有一種方法可以解決這個問題,而不需要從ThreadPool
開始新線程(我嘗試過這樣做,並且使用線程池可以按預期方式工作,如果從ThreadPool線程調用DownloadStringAsync,webClient的回調也會按預期發生。
ASP.NET MVC 3.0,.NET 4.0,MS卡西尼開發Web服務器(VS 2010)
編輯:這裏是一個全碼:
public class HomeController : Controller {
private static ManualResetEvent done;
public ActionResult Index() {
return Content(DownloadString() ? "success" : "failure");
}
private static bool DownloadString() {
try {
done = new ManualResetEvent(false);
var wc = new WebClient();
wc.DownloadStringCompleted += (sender, args) => {
// this breakpoint is not hit until after Index() returns.
// It is weird though, because response isn't returned to the client (browser) until this callback finishes.
// Note: This thread is different from one Index() was running on.
done.Set();
};
var uri = new Uri(@"http://us.battle.net/wow/en/character/blackrock/hunt/simple");
wc.DownloadStringAsync(uri);
var timedout = !done.WaitOne(3000);
if (timedout) {
wc.CancelAsync();
// if this would be .WaitOne() instead then deadlock occurs.
var timedout2 = !done.WaitOne(3000);
Console.WriteLine(timedout2);
return !timedout2;
}
return true;
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
return false;
}
}
這說明了這一點。那麼,WebClient不使用ThreadPool? WebClient如何排隊請求?我用反射器將它反射了一段時間,但找不到它發生的地方。 – 2011-04-19 15:51:01
它不排隊請求。它實際上是在那時開始請求。但是在啓動請求後,DownloadStringAsync()將返回並讓您在下載過程中執行其他操作。在下載過程中,實際上根本沒有線程!它只會從線程池中拉出一個線程來通知您完成。 – RandomEngy 2011-04-19 15:54:56
我明白了。那麼在代碼中的回調被調用?我試圖在從Index返回之前放置Thread.Sleep(10000),但直到索引返回之前,回調仍然不會被調用。這意味着回調呼叫在索引調用後排隊。我很好奇這是怎麼發生的。如果在CLR ThreadPool上調用回調函數,它不應該被GET請求處理線程阻塞,是嗎? – 2011-04-19 15:59:24