none
非同期ラムダ式のコードの書き方について RRS feed

  • 質問

  • お世話になります。

    非同期ラムダ式のコードの記述方法について教えて下さい。

    ためしに、テストで以下のコードを書き実行したところ、以下の結果になりました。

    【テストコード】

    private void btn_1_Click(object sender, EventArgs e)
    {
    Func<string, Task<string>> fc =  async (txt) =>
    {
    await Task.Run(() =>
    {
    Task.Delay(1000).Wait();
    });

    Console.WriteLine("★");
    return txt;
    };
    Console.WriteLine(fc("finish"));
    }

    【結果】

    System.Threading.Tasks.Task`1[System.String]

    本来、希望した結果は

    ★→Finishの順番なのですか、思うような結果になりません。

    ためしにConsole.WriteLine(fc("finish"))をConsole.WriteLine(fc("finish").Status)にしたところ

    「WaitingForActivation」でした。

    非同期ラムダ式を使って、本来、ほしい結果を出すにはどうしたらよろしいでしょうか。

    それと、もう1件、Actionの戻り値無しのエラー取得についてお聞きします。

    C#で書いた以下のコードと同じ構成でVBで記述しましたが、エラーを受け取れません。

    どうすれば受け取れるでしょうか。

    【テストコードC#】

    Action<string> ac = async (txt) =>
    {
    await Task.Run(() =>
    {
    Console.WriteLine(int.Parse(txt));
    }).ContinueWith((c) => {
    if (c.Exception != null)
    {
    Console.WriteLine("【ContinueWith】" + c.Exception.InnerException.Message);
    }
    });
    Console.WriteLine(txt);
    };
    ac("Finish");

    【結果】

    【ContinueWith】入力文字列の形式が正しくありません。
    Finish

    これをVBで、同じ結果を出力したい。

    以上、よろしくお願いします。

    2020年11月13日 0:26

回答

  • 1つ目、fcの返値はTask<string>なので、そのタスクの結果を受け取りたいならawaitする必要があります。

    async void btn_1_Click(...) { // ここにasyncが必要
        // fcの定義略
        Console.WriteLine(await fc("finish")); // fcが返すTask<string>をawait
    }
    2つ目、なぜわざわざContinueWithを使っているのでしょうか。await周りをTry-Catchしたほうがいいと思います。

    Dim ac As Action(Of String)
    ac = Async Sub(txt)
        Try
            Await Task.Run(Sub() Cnosole.WriteLine(Int32.Parse(txt)))
        Catch ex As Exception
            Console.WriteLine("【catch】" + ex.Message)
        End Try
        Console.WriteLine(txt)
    End Sub
    ac("Finish")
    非同期処理があるなら、返値は常にTaskにしておく(つまり、Action<T>ではなくFunc<T, Task>にする)ことをお勧めします。
    • 回答としてマーク mexicanafro 2020年11月13日 1:44
    2020年11月13日 1:08

すべての返信

  • 1つ目、fcの返値はTask<string>なので、そのタスクの結果を受け取りたいならawaitする必要があります。

    async void btn_1_Click(...) { // ここにasyncが必要
        // fcの定義略
        Console.WriteLine(await fc("finish")); // fcが返すTask<string>をawait
    }
    2つ目、なぜわざわざContinueWithを使っているのでしょうか。await周りをTry-Catchしたほうがいいと思います。

    Dim ac As Action(Of String)
    ac = Async Sub(txt)
        Try
            Await Task.Run(Sub() Cnosole.WriteLine(Int32.Parse(txt)))
        Catch ex As Exception
            Console.WriteLine("【catch】" + ex.Message)
        End Try
        Console.WriteLine(txt)
    End Sub
    ac("Finish")
    非同期処理があるなら、返値は常にTaskにしておく(つまり、Action<T>ではなくFunc<T, Task>にする)ことをお勧めします。
    • 回答としてマーク mexicanafro 2020年11月13日 1:44
    2020年11月13日 1:08
  • 何か目的があってそれを実現するためのコードの一部だと思いますが、そもそも何がしたいのかの目的を書けませんか。

    btn_1_Click と書いてあるところを見ると GUI アプリのようですが Console.WriteLine を使っていたり(GUI アプリとコンソールアプリは SynchronizationContext などが違います)、await を使っているのに async がなかったり、await Task.Run(() => {Task.Delay(1000).Wait(); }); とか訳の分からないコードがあったりと、はっきり言わせていただけるとコメントしても意味がなさそうとしか自分には思えないのです。

    そもそもの目的が分かれば、その方向でどうできるかのコメントができるかもしれません。(できなかったらすみませんが)

    それから、何を作っているか(Windows Forms? WPF? ASP.NET Web アプリ? その他?)と開発環境(OS, .NET Framework, Visual Studio のバージョンなど)も書いてください。。


    • 編集済み SurferOnWww 2020年11月13日 1:27 追記
    2020年11月13日 1:17
  • ご回答ありがとうございます。

    >非同期処理があるなら、返値は常にTaskにしておく(つまり、Action<T>ではなくFunc<T, Task>にする)ことをお勧めします。

    了解です。勉強になりました。

    2つ目の件は、await-asyncの外で例外受け取りテストをしたとき、自分のコードの書き方がまずかったのか受け取れなかったので、これでどうにか結果が取れたので、vbではどうなんだろうと思いまして。

    特に、こだわり等は全くございません。

    今回は本当にありがとうございました。

    2020年11月13日 1:45
  • ご回答ありがとうございます。

    質問への回答になるかわかりませんが、非同期ラムダ式をネットで見まして、例外等が発生したらどこで受け取るんだろうと思いテストしていたところ、上記の問題が出まして。

    自分じゃ回答まで導けそうになかったので、ここで質問させて頂きました。

    あと、今の環境につきましては

    Windows Form、Visual Studioのバージョンはコミュニティの2019になります。(学習用環境)

    以上になります。


    2020年11月13日 1:56
  • > 非同期ラムダ式をネットで見まして、例外等が発生したらどこで受け取るんだろうと思いテストしていたところ、上記の問題が出まして。

    ラムダ式と普通のメソッドの違いは、少なくとも本質的・基本的なところに関しては関係なさそうです。非同期で動くメソッドで発生した例外は await, async, Task をきちんと使っていれば呼び出し側で捕捉できます。

    というわけで、ラムダ式うんぬんよりも、await, async, Task の使い方を勉強した方が良いと思います。

    例えば昔のデリゲートを利用した非同期メソッドを使った場合、例外はコールバックもしくは集約的例外ハンドラの中でしか捕捉できませんが、.NET 4.5 以降であれば await, async, Task を使って呼び出し側で捕捉できます。具体例は以下の記事を見てください。

    デリゲートを利用した非同期メソッドの実装
    http://surferonwww.info/BlogEngine/post/2019/06/19/coding-asynchronous-method-by-using-delegate-in-windows-forms-application.aspx

    以下の記事も参考になると思いますので、読むことをお勧めします。

    非同期プログラミングのベスト プラクティス
    https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming


    > Windows Form、Visual Studioのバージョンはコミュニティの2019になります。(学習用環境)

    .NET Framework のバージョンも重要な情報ですので必ず書くようにしてください。Core の場合はそのこととバージョンも書いてください。

    2020年11月13日 2:37