15

以下のプログラムの開始後に、何らかの理由で一時停止があります。WebClient().DownloadStringTaskAsync()それが原因だと思います。

class Program
{
    static void Main(string[] args)
    {
        AsyncReturnTask();

        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

    public static async void AsyncReturnTask()
    {
        var result = await DownloadAndReturnTaskStringAsync();
        Console.WriteLine(result);
    }

    private static async Task<string> DownloadAndReturnTaskStringAsync()
    {
        return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));
    }
}

私が理解している限り、私のプログラムはすぐに 0 から 15 までのカウントを開始する必要があります。私は何か間違ったことをしていますか?

元の Netflix ダウンロード サンプル ( CTPで取得) でも同じ問題がありました。検索ボタンを押した後、UI が最初にフリーズし、しばらくすると、次の映画の読み込み中に応答します。そして、PDC 2010 での Anders Hejlsberg のプレゼンテーションでは、それは固まらなかったと思います。

もう一つ。代わりに

return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));

私は自分の方法を使用します:

return await ReturnOrdinaryTask();

それは次のとおりです。

public static Task<string> ReturnOrdinaryTask()
{
    var t = Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("------------- " + i.ToString());
            Thread.Sleep(100);
        }
        return "some text";
    });
    return t;
}

正常に動作します。つまり、何もロードされませんが、すぐに開始され、作業中にメインスレッドをブロックしません。

編集

OK、私が今信じているのは、WebClient.DownloadStringTaskAsync機能が台無しになっているということです。次のように、最初のブロック期間なしで機能するはずです。

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        Task.Factory.StartNew(() =>
            {
                cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
                cli.DownloadStringAsync(new Uri("http://www.weather.gov"));
            });

        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }
4

3 に答える 3

15

プログラムはしばらくブロックしますが、結果がリモート サーバーから返される前に for ループで実行を再開します。

新しい非同期 API はまだシングルスレッドであることを思い出してください。そのWebClient().DownloadStringTaskAsync()ため、リクエストが準備されてサーバーに送信されるまで、スレッドで実行する必要があります。その後await、Main() のプログラム フローに実行を戻すことができます。

表示されている結果は、マシンからリクエストを作成して送信するのに時間がかかるためだと思います。最初にそれが完了すると、 の実装はDownloadStringTaskAsyncネットワーク IO とリモート サーバーが完了するのを待ち、ユーザーに実行を返すことができます。

一方、あなたのRunOrdinaryTaskメソッドは単にタスクを初期化し、ワークロードを与え、開始するように指示します。その後、すぐに戻ります。を使用しても遅延が見られないのはそのためですRunOrdinaryTask

このテーマに関するいくつかのリンクがあります: Eric Lippert のブログ(言語設計者の 1 人)、およびそれに関するJon Skeet の最初のブログ投稿。Eric は、継続渡しスタイルに関する一連の 5 つの投稿を行っていasyncますawait。新機能を詳しく理解したい場合は、Eric の CPS と Async に関する投稿を読むことをお勧めします。とにかく、上記の両方のリンクは、非常に重要な事実をうまく説明しています。

  • 非同期 != 並列

つまり、asyncandawaitは新しいスレッドを作成しません。ブロッキング操作を実行しているときに、通常のフローの実行を再開できるようにするだけです-CPUが同期プログラムで何もせず、外部操作が完了するのを待っているとき。

編集

何が起こっているのかを明確にするためにDownloadStringTaskAsync、継続を設定しWebClient.DownloadStringAsync、同じスレッドで を呼び出してから、実行をコードに返します。したがって、ループがカウントを開始する前に表示されるブロック時間は、DownloadStringAsync完了するまでにかかる時間です。async と await を使用したプログラムは、次のプログラムとほぼ同等であり、プログラムと同じ動作を示します: 最初のブロック、次にカウントが開始され、途中で非同期操作が終了し、要求された URL:

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
        cli.DownloadStringAsync(new Uri("http://www.weather.gov")); // Blocks until request has been prepared
        
        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

注: 私はこの件については専門家ではありませんので、いくつかの点で間違っている可能性があります。これが間違っていると思われる場合は、この件に関する私の理解を自由に修正してください。昨夜、PDC プレゼンテーションを見て、CTP で遊んだところです。

于 2010-10-30T09:37:14.237 に答える
5

この問題は、IE / Registry / Somewhere Slowから検出されたプロキシ構成設定に関連していないと思いますか?

webClient.Proxy = nullを設定してみてください(またはapp.configで設定を指定してください)。「ブロック」期間は最小限に抑える必要があります。

于 2011-02-02T15:58:23.173 に答える
2

F5またはCTLR+F5を押して実行していますか?F5では、VSがAsyncCtpLibrary.dllのシンボルを検索するだけで遅延が発生します...

于 2010-10-30T19:22:37.727 に答える