3
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 タスクを使用しており、フレームワーク 4.5 に切り替えるまで、WebClient のメソッドの一部をラップする必要がありました。

上記のメソッドDownloadStringTaskAsyncは、これらのラッパーの 1 つの単純化されたバージョンです。

私が理解できないのは、タスクを待機すると aspnet のスレッドがデッドロックし、コンソールまたはデスクトップ アプリケーションで正常に動作する理由です。

デッドロックされたスレッドは次のようになります。

リクエストスレッド

[スリープ中、待機中、参加中]
mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout, bool exitContext) + 0x18 バイト mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout, System .Threading.CancellationToken cancelToken) + 0x264 バイト
mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int ミリ秒タイムアウト、System.Threading.CancellationToken cancelToken) + 0x166 バイトint millisecondsTimeout, System.Threading.CancellationToken cancelToken) + 0x41 バイト
mscorlib.dll!System.Threading.Tasks.Task.Wait() + 0xb バイト
TCSProblem.DLL!TCSProblem.Controllers.HomeController.Index() 16 行目 + 0xa バイト

WebClient スレッド

[スリープ中、待機中、または参加中]
mscorlib.dll!System.Threading.Monitor.Enter(object obj, ref bool lockTaken) + 0x14 バイト
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、オブジェクト arg) + 0x29 バイト
System.dll!System.Net.WebClient.PostProgressChanged(System.ComponentModel.AsyncOperation asyncOp、System.Net.WebClient.ProgressData 進行状況) + 0x25c バイト

4

2 に答える 2

11

残念ながら、WebClient は現在の同期コンテキストをキャプチャし、それにポストすることで完了イベントを発生させます。そのため、完了アクションは、決して起こらないアクション メソッドから戻った後にのみ実行されます。つまり、あなたのどちらか...

  1. ... synccontext を持たないスレッドに切り替えるには、DownloadStringAsync への呼び出しを Task.Factory.StartNew にラップする必要があります
  2. ...呼び出す前に SynchronizationContext.Current を null に設定し、後で復元する必要があります
  3. ...非同期 MVC アクションを使用する
于 2012-06-28T11:33:49.657 に答える