MetroStyleApp入門 vol31.await / asyncについて

日曜日 , 8, 7月 2012 Leave a comment

(この記事はWindows 8 RP、VisualStudio2012RCで確認されています)

 

 await / async構文は非同期処理をあたかも同期処理のフローのように記述できるため非常に便利な構文ですが、やっぱりちゃんと理解してないとはまります。

 例えば非同期処理の呼び出しを順序立てて行う必要がある場合。

 

 実験のために応答時間の異なる3つのAPI(ともいえないもの)を用意します。

 

 ・http://coelacanth.heteml.jp/win8/api_test/a.php

 応答に3秒かかります。aを出力します。

 

 ・http://coelacanth.heteml.jp/win8/api_test/b.php

 ほぼ即座に応答します。bを出力します。

 

 ・http://coelacanth.heteml.jp/win8/api_test/c.php

 応答に2秒かかります。cを出力します。

 

 まずはシンプルに以下のようなコードを実行します。

 

            var http = new HttpClient();
            Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/a.php");
            var response = await http.GetAsync(uri);
            System.Diagnostics.Debug.WriteLine(await response.Content.ReadAsStringAsync());

            var http2 = new HttpClient();
            Uri uri2 = new Uri("http://coelacanth.heteml.jp/win8/api_test/b.php");
            var response2 = await http2.GetAsync(uri2);
            System.Diagnostics.Debug.WriteLine(await response2.Content.ReadAsStringAsync());

            var http3 = new HttpClient();
            Uri uri3 = new Uri("http://coelacanth.heteml.jp/win8/api_test/c.php");
            var response3 = await http3.GetAsync(uri3);
            System.Diagnostics.Debug.WriteLine(await response3.Content.ReadAsStringAsync());

 

 このコードを実行するとa→b→cという結果を得られます。呼び出しが同期的に行われていることがわかります。

 続いて各呼び出しを関数として切り出します。

 

            this.getA();
            this.getB();
            this.getC();

 

        private async void getC()
        {
            var http = new HttpClient();
            Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/c.php");
            var response = await http.GetAsync(uri);
            System.Diagnostics.Debug.WriteLine(await response.Content.ReadAsStringAsync());
        }

        private async void getB()
        {
            var http = new HttpClient();
            Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/b.php");
            var response = await http.GetAsync(uri);
            System.Diagnostics.Debug.WriteLine(await response.Content.ReadAsStringAsync());
        }

        private async void getA()
        {
            var http = new HttpClient();
            Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/a.php");
            var response = await http.GetAsync(uri);
            System.Diagnostics.Debug.WriteLine(await response.Content.ReadAsStringAsync());
        }

 

 予想通りこの呼び出しは順次実行されません。得られる出力は以下です。

 b→c→c

 まさしく非同期な結果ですね。

 

 では同期的に呼び出したい場合(awaitをつけて呼び出したい場合)どのように書くかというと以下のようにTaskを利用します。

 

            var a = await this.getTaskA();
            System.Diagnostics.Debug.WriteLine(a);

            var b = await this.getTaskB();
            System.Diagnostics.Debug.WriteLine(b);

            var c = await this.getTaskC();
            System.Diagnostics.Debug.WriteLine(c);

 

 

        private Task<string> getTaskC()
        {
            return Task.Run(async () =>
            {
                var http = new HttpClient();
                Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/c.php");
                var response = await http.GetAsync(uri);
                string str = await response.Content.ReadAsStringAsync();
                return str;
            }
            );
        }

        private Task<string> getTaskB()
        {
            return Task.Run(async () =>
                {
                    var http = new HttpClient();
                    Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/b.php");
                    var response = await http.GetAsync(uri);
                    string str = await response.Content.ReadAsStringAsync();
                    return str;
                }
            );
        }


        private Task<string> getTaskA()
        {
            return Task.Run(async () =>
                {
                    var http = new HttpClient();
                    Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/a.php");
                    var response = await http.GetAsync(uri);
                    string str = await response.Content.ReadAsStringAsync();
                    return str;
                }
            );
        }

 

 これでa→b→cの出力が得られました。

 

 今回のサンプルのDLはこちら(85.3kb)

 

追記:コメントでneueccさんに指摘いただきました。

 

        private async Task getTaskA()
        {
            var http = new HttpClient();
            Uri uri = new Uri("http://coelacanth.heteml.jp/win8/api_test/a.php");
            var response = await http.GetAsync(uri);
            string str = await response.Content.ReadAsStringAsync();
            System.Diagnostics.Debug.WriteLine(str);
            return;
        }

 

 この書き方だと、呼び出した側にawaitが来て、

 

await this.getTaskA();

 

 勉強になります!!

 (まだawait / asyncやTaskについては知識不足で勉強することがいっぱいだ)

 


匿名 へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください