3

私はasyncキーワードをテストするためだけにこの非同期コードを試しています:

public async Task<string> AsyncMethod()
{
    var link = "http://www.google.com";

    var webclient = new WebClient();
    var result = await webclient.DownloadStringTaskAsync(new Uri(link));

    return result;
}

public async Task<ActionResult> Index()
{
    var a = AsyncMethod();
    var b = AsyncMethod();

    Task.WaitAll(a, b);

    return View();
}

しかし、デバッグすると、デバッガーはにヒットし、Task.WaitAll何もしません(リターンキーワークは実行されません)。2つの「AsyncMethod」の前に待機を設定し、Task.WaitAllそれを削除すると、機能します。

4

3 に答える 3

12

メソッドは ASP.NET MVC コントローラー アクションのように見えるため、ASP.NET で実行していると想定しています。

デフォルトでは、非同期メソッドは中断された同じコンテキスト (つまり、 を呼び出した場所await) で再開されます。ASP.NET では、これは現在の要求コンテキストを意味します。また、一度に特定のコンテキストに存在できるスレッドは 1 つだけです。そのため、実行されるスレッドIndex()はリクエスト コンテキスト内にあり、ブロックされWaitAll()ます。一方、 の両方の呼び出しは、同じコンテキストで (ダウンロードの完了後に) 再開しようとしていますが、がそのコンテキストでまだ実行されているAsyncMethod()ため、再開できません。Index()このため、メソッドはデッドロック状態にあり、何も起こりません。

(GUI コンテキストはこの点で同様に動作するため、同じデッドロックが GUI アプリケーションでも発生します。コンソール アプリケーションにはコンテキストがないため、この問題はありません。)

これに対する修正は 2 つあります。

  1. メソッドを同期的に待機しないでくださいasyncMain()(おそらく唯一の例外は、コンソール アプリケーションのメソッドから非同期メソッドを実行する場合です。)

    代わりに、非同期で待機します。あなたの場合、それはawait Task.WhenAll(a, b).

  2. ConfigureAwait(false)「ライブラリ」メソッド (つまり、実際にはリクエスト コンテキストで実行する必要がないもの) で使用します。

1 つまたは 2 つを使用すると問題が解決しますが、両方を使用するのがおそらく最善です。

この問題の詳細については、Stephen Cleary の記事Don't Block on Async Code を参照してください。

于 2012-12-26T22:30:37.960 に答える
-2

次のように動作します。

public Task<string> FakeAsyncMethod()
{
    var link = "http://google.com";
    var webclient = new WebClient();
    var t = new Task<string>(() => webclient.DownloadString(new Uri(link)));
    return t;
}

public async Task Index()
{
    var a = FakeAsyncMethod();
    var b = FakeAsyncMethod();
    a.Start();
    b.Start();
    Task.WaitAll(a, b);
}

async void AsyncCall()
{
    await Index();
}

なぜあなたのメソッドで動作しないのかわかりませんが、asyncキーワードでマークされたメソッドによって返されるタスクが実行中の状態で作成されるためだと思います (より正確には、Statusequals to でWaitingForActivation)。もっと調べてみます。

編集: 別の方法は、キーワードTask.WhenAllと組み合わせて使用​​することです。await

public async Task Index()
{
    var a = AsyncMethod();
    var b = AsyncMethod();
    await Task.WhenAll(a, b);
}
于 2012-12-26T21:53:07.440 に答える