WebClient asynchronous callback not called in ASP.NET MVC

In a GET request, I run (something like):

public ActionResult Index(void) { webClient.DownloadStringComplete += onComplete; webClient.DownloadStringAsync(...); return null; } 

I see that onComplete does not start until Index() . I see that onComplete is being called in another thread from one Index .

Question: Why is this happening? why does the webClient asynchronous thread seem to be blocked until the request processing thread ends?

Is there a way to fix this without starting a new thread from ThreadPool (I tried this and using the thread pool works as expected. Also, the WebClient callback is executed as expected if DownloadStringAsync is called from the ThreadPool thread).

ASP.NET MVC 3.0, .NET 4.0, MS Cassini dev Web Server (VS 2010)

EDIT: Here is the complete code:

 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; } } 
+5
multithreading asp.net-mvc
source share
3 answers

I was curious about this, so I asked the ASP.NET internal alias for discussion and got a response from Levi Broderick:

ASP.NET internally uses SynchronizationContext for synchronization, and only one thread is ever allowed to have control of this lock. In your specific example, the thread executed by HomeController :: DownloadString contains a lock, but ManualResetEvent is waiting for it to start. ManualResetEvent will not be launched until the DownloadStringCompleted method is executed, but this method works on another thread that will never lock synchronization, since the first thread still holds it. You are now at a standstill.

I am surprised that this has ever worked MVC 2, but if it was made a lucky break. This has never been supported.

+5
source share

This is the point of using asynchronous processing. Your main thread launches the call, then continues to do other useful things. When the call is completed, it selects a thread from the I / O completion thread pool and calls the registered callback method on it (in this case, the onComplete method). This way, you do not need to have an expensive stream waiting for the end of a long web call.

In any case, the methods you use follow an asynchronous event-based pattern. You can read more about this here: http://msdn.microsoft.com/en-us/library/wewwczdw.aspx

(edit) Note: ignore this answer as this will not help to answer the clarified question. Leaving him for the discussion that took place under him.

+1
source share

In addition to the answer chosen, see this article for more information on why WebClient captures the SynchronizationContext.

http://msdn.microsoft.com/en-gb/magazine/gg598924.aspx

0
source share

All Articles