はかせのラボ

私の頭の中を書いていく雑記ブログです

Unity C# async/await 初歩

あいさつ

どうも、はかせです。
最近ずっとDirectX11をやってきました。
ただ1つの節目を過ぎコードのまとめとかしてるだけなので
正直そこについて書くことがありませんorz
(そんなんせず開発進めちゃえよって?そんなことしたら泣きを見るのは後の自分なんだよ・・・)

ということで今回はUnity+C#で使えるasync/awaitです。
今更って思った方いますよね?確かにそうなんです。
ただ私は2.3ヶ月触ってなかったので最新のイケてるやつとか無理なんですorz
あと何故かあんま浸透してないように思うんですよね・・・async/await。
なので私のメモ兼ちょっとした布教用ですw

そもasync/awaitって何よ?

C#5.0から登場した非同期キーワードです。
まずはそれぞれ意味を見ていきましょう。
・async
 awaitキーワードを使えるようにする。
・await
 Taskの完了を待つ。Taskの結果を取り出す。

使ってみる

習うより慣れよ、まずはスクリプトです。

//async/awaitのテスト用 名前適当なのは許して・・・
public class AsyncAwaitTest : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        Debug.Log("FirstAsync Start!");
        FirstAsync();
    }

    private async Task FirstAsync()
    {
        await Task.Delay(1000);
        Debug.Log("FirstAsync Done!");
    }
}

よくある1秒待ってログ出すやつです。
それでは実行結果です。
f:id:hakase0274:20181206231214g:plain

無事1秒後にログが出ました。

なんか出てる

この書き方だと警告が出ます。
f:id:hakase0274:20181206231823p:plain

awaitできるんだからしたら?っていう警告です。
無視しても害は特にないですが警告出てるのは気持ち悪いです。

解決方法

解決方法は私が知ってる限りで3通りあります。
①awaitする
素直に指示に従います。

//async/awaitのテスト用 名前適当なのは許して・・・
public class AsyncAwaitTest : MonoBehaviour
{

    // Use this for initialization
    async void Start()
    {
        Debug.Log("FirstAsync Start!");
        await FirstAsync();
    }

    private async Task FirstAsync()
    {
        await Task.Delay(1000);
        Debug.Log("FirstAsync Done!");
    }
}

これで消えます。ただasync voidが出てしまいました。
async voidは原則良くないとされています。
async Taskであれば結果が返ってくるのでいかようにも出来ますが、
voidなのでなにも戻ってきません。つまり処理のハンドリングができません。
なんでよくないわけですね。

②変数で受け取る
awaitはTaskを待ち結果を取り出すキーワードです。
待たなくても良いならばawaitではなく素直に変数受け取りで解決できます。

//async/awaitのテスト用 名前適当なのは許して・・・
public class AsyncAwaitTest : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        Debug.Log("FirstAsync Start!");
        var task = FirstAsync();
    }

    private async Task FirstAsync()
    {
        await Task.Delay(1000);
        Debug.Log("FirstAsync Done!");
    }
}

別によくないとか言われてることはしてないので問題はないのですが
ただ受け取るためだけの変数がいるのは気持ち悪いです・・・

③拡張メソッドでごまかす
こんな感じの拡張メソッドを用意します。

public static async void FireForget(this Task task)
{
    try
    {
        await task;
    }
    catch(Exception e)
    {
        Debug.LogError(e);
    }
}

つなげたTaskを待っているだけです。
それでさっきのやつを

//async/awaitのテスト用 名前適当なのは許して・・・
public class AsyncAwaitTest : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        Debug.Log("FirstAsync Start!");
        FirstAsync().FireForget();
    }

    private async Task FirstAsync()
    {
        await Task.Delay(1000);
        Debug.Log("FirstAsync Done!");
    }
}

こんな感じで繋げます。
これで受け取り専門変数もasync voidもなく警告を消せます。

結局どうする?

正直好みかなーっと思います。
どれもやってること大差ないので。

なので好みとコーディング規約に従って解決しましょう。
ちなみに私はUnityのStartとかで呼ぶならasync void、
それ以外は変数受け取りでやってます。

見た目は拡張メソッドが一番なのですが、
わざわざ基本無害の警告のために拡張メソッド用意して繋げるのは
めんどくさいので・・・・

あとがき

async/await初歩でした。
とはいえ使い方なんてのはこれくらいで後は応用でごにょる方法がいくつかある程度です。

こんな風に簡単で使いやすいので皆さんも是非使いましょう!

それでは今回はこの辺でノシ