ASP.NET MVC中的WebClient死锁(同步动作方法)

时间:2012-06-28 11:23:59

标签: c# asp.net-mvc asynchronous task-parallel-library

public class HomeController : Controller
{
    public ActionResult Index()
    {
        Task<string> t = DownloadStringTaskAsync(new Uri("http://www.google.com/search?q=aspnet"));
        t.Wait();
        string res = t.Result;

        return View((object)res);
    }

    Task<string> DownloadStringTaskAsync(Uri address)
    {
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += (s, e) => tcs.SetResult(e.Result);
        wc.DownloadStringAsync(address);
        return tcs.Task;
    }
}

该操作永远不会返回,请求只会超时。

我们正在为所有异步操作使用TPL任务,并且必须包含一些WebClient的方法,直到我们切换到框架4.5。

上述方法DownloadStringTaskAsync是其中一个包装器的简化版本。

我无法弄清楚为什么等待任务会使aspnet中的线程死锁并在控制台或桌面应用程序中正常运行。

以下是死锁线程的样子。

  

请求线程

     

[在睡觉,等待或加入]
  mscorlib.dll!System.Threading.Monitor.Wait(object obj,int millisecondsTimeout,bool exitContext)+ 0x18 bytes   mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)+ 0x264 bytes
  mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)+ 0x166 bytes   mscorlib.dll!System.Threading.Tasks.Task.Wait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)+ 0x41 bytes
  mscorlib.dll!System.Threading.Tasks.Task.Wait()+ 0xb bytes
  TCSProblem.DLL!TCSProblem.Controllers.HomeController.Index()第16行+ 0xa字节

     

WebClient线程

     

[在睡觉,等待或加入]
  mscorlib.dll!System.Threading.Monitor.Enter(object obj,ref bool lockTaken)+ 0x14 bytes
  System.Web.dll!System.Web.AspNetSynchronizationContext.CallCallback(System.Threading.SendOrPostCallback回调,对象状态)+ 0x53字节
  System.Web.dll!System.Web.AspNetSynchronizationContext.Post(System.Threading.SendOrPostCallback回调,对象状态)+ 0x9字节
  System.dll!System.ComponentModel.AsyncOperation.Post(System.Threading.SendOrPostCallback d,object arg)+ 0x29 bytes
  System.dll!System.Net.WebClient.PostProgressChanged(System.ComponentModel.AsyncOperation asyncOp,System.Net.WebClient.ProgressData progress)+ 0x25c bytes

2 个答案:

答案 0 :(得分:10)

不幸的是,WebClient捕获当前的同步上下文并通过发布它来引发完成事件。因此,完成操作仅在您从操作方法返回后才会运行,这将永远不会发生。这意味着,你要么......

  1. ...需要在Task.Factory.StartNew中包含对DownloadStringAsync的调用,以便切换到没有synccontext的线程
  2. ...需要在调用之前将SynchronizationContext.Current设置为null,然后将其恢复
  3. ...使用异步MVC操作

答案 1 :(得分:-1)

所有.NET类默认捕获SynchronizationContext,我遇到了类似的问题:SmtpClient.SendAsync blocking my ASP.NET MVC Request