40
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Unity】非同期処理を理解する〜async/await編〜

Posted at

async/await とは

  • C# の機能として用意されている
    • C# 5.0の新機能
    • Unity 2018 から C# 6.0 に対応した(それまでは C# 4.0 でした。)
    • Task クラスに対して使うものという軽い認識(本当は INotifyCompletion インターフェースの IsCompletedGetResult
  • Unity で .NET 4.x を選択すれば使用可能
  • async = asynchronous = 非同期
    • await を使うメソッドにつけなければならないキーワード
    • async をつけただけでは普通のメソッドと挙動は変わらない
  • await
    • 非同期メソッドを呼び出し、完了するまで実行を中断するキーワード
  • 戻り値を取得できる(コルーチンは無理)
  • 並列処理だけど、普通のメソッドの呼び出しと同じようにかける = 可読性が上がる

基本的な構文は、

async 戻り値の型 メソッド名(引数)
{
    await ~~~~
}    

戻り値は、基本的に Task / Task<T> 型と考えて問題ないかと思います。
(といいながら、サンプルは void で書いたりしています…)

機能

Task.Delay(Int32 millisecondsDelay)

引数で指定されたミリ秒待機します。

using System.Threading.Tasks;
using UnityEngine;


public class Test : MonoBehaviour
{
    void Start()
    {
        AsyncSample1();
    }

    async void AsyncSample1()
    {
        Debug.Log("AsyncSample1 Start.");
        await Task.Delay(1000);
        Debug.Log("AsyncSample1 End.");
    }
}

今回のサンプルでは、1000ミリ秒 = 1秒 待機するようにしたので、実行結果は以下のようになります。
image.png

ContinueWith

直列処理できます。
非同期処理のメソッドも、ContinueWith でつなぐことで、直列化することができます。

ちなみに ContinueWith使わずに、普通に並列的に処理にしたければ、下のようになイメージになります。

using System.Threading.Tasks;
using UnityEngine;


public class Test : MonoBehaviour
{
    void Start()
    {
        AsyncSample1();
        AsyncSample2();
    }

    async void AsyncSample1()
    {
        Debug.Log("AsyncSample1 Start.");
        await Task.Delay(1000);
        Debug.Log("AsyncSample1 End.");
    }

    async void AsyncSample2()
    {
        Debug.Log("AsyncSample2 Start.");
        await Task.Delay(1000);
        Debug.Log("AsyncSample2 End.");
    }
}

並列なので、

  1. AsyncSample1 Start.
  2. AsyncSample2 Start.
  3. AsyncSample1 End.
  4. AsyncSample2 End.
    という結果になります。

image.png

ContinueWith を使って直列処理(AsyncSample1 を実行完了後、 AsyncSample2 を実行)するサンプルは、

using System.Threading.Tasks;
using UnityEngine;


public class Test : MonoBehaviour
{
    void Start()
    {
        AsyncSample1().ContinueWith(_ => AsyncSample2());
    }

    async Task AsyncSample1()
    {
        Debug.Log("AsyncSample1 Start.");
        await Task.Delay(1000);
        Debug.Log("AsyncSample1 End.");
    }

    async Task AsyncSample2()
    {
        Debug.Log("AsyncSample2 Start.");
        await Task.Delay(1000);
        Debug.Log("AsyncSample2 End.");
    }
}

注意点は、メソッドの返り値の型が Task になっていることくらいです。

  1. AsyncSample1 Start.
  2. AsyncSample1 End.
  3. AsyncSample2 Start.
  4. AsyncSample2 End.
    という結果になります。

image.png

Task.Run()

引数として与えられた処理を別スレッドで実行します。
用途として、重い処理を非同期にしたいときに使います。

ちょっと良い例が思いつかないので、超簡単なサンプルだけ書きます。

Task.Run(()  =>  Debug.Log("重い処理..."));

Task.WhenAll()

指定された Task が全て完了してから Task を実行することができます。

using System.Threading.Tasks;
using UnityEngine;


public class Test : MonoBehaviour
{
    async void Start()
    {
        await Task.WhenAll(AsyncSample1(), AsyncSample2());
        Debug.Log("All Completed.");
    }

    async Task AsyncSample1()
    {
        Debug.Log("AsyncSample1 Start.");
        await Task.Delay(1000);
        Debug.Log("AsyncSample1 End.");
    }

    async Task AsyncSample2()
    {
        Debug.Log("AsyncSample2 Start.");
        await Task.Delay(1000);
        Debug.Log("AsyncSample2 End.");
    }
}

AsyncSample1(), AsyncSample2() の実行が完了したら、 All Completed. を出力するようなサンプルです。
実行結果は、
image.png

Task<T>

戻り値が欲しい場合は Task<T> を使います。

using System.Threading.Tasks;
using UnityEngine;


public class Test : MonoBehaviour
{
    async void Start()
    {
        var str = await AsyncSample1();
        Debug.Log(str);
    }

    async Task<string> AsyncSample1()
    {
        Debug.Log("AsyncSample1 Start.");
        await Task.Delay(1000);
        return "AsyncSample1 End.";
    }
}

AsyncSample1() の結果が返ってくるまで、Debug.Log されると困るので、 await する必要があります。
(待ち受けするイメージです。)
結果は、
image.png

終わりに

Unity 初心者なので、間違いがあったら教えてくれるとありがたいです。
Unity で Task 使うなら、 UniTask 使おう。(理由はいまいち知らない…)
理由は UniTask の記事を書くときに、調べたいと思います。

参考文献

https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task?view=netcore-3.1
https://torikasyu.com/?p=1554
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task.whenall?view=netcore-3.1
https://www.slideshare.net/UnityTechnologiesJapan/unite-tokyo-2018asyncawait

40
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?