NinaLabo

個人ゲーム開発者の技術メモ

【Unity】 async/awaitの実行スレッド

C#6.0から async/await が使えるようになり非同期処理がスッキリ書けそうなので興味あるのですが、マルチスレッドとの関連が不明だったので色々試してみました。

実行環境

Unity2018.3.4f1

Taskのasync/await

まずはシンプルなasync/awaitのサンプル。TestAsyncで3秒待っています。

public class TaskTest1 : MonoBehaviour
{
    private async void Start()
    {
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call Start");
        await TestAsync();
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Start");
    }

    public async Task TestAsync()
    {
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call TestAsync");
        await Task.Delay(3000);
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End TestAsync");
    }
}

 結果はこんな感じですべて同じスレッドで実行されます。

f:id:ninagreen:20190203050126p:plain

つまり、async/awaitってただ書くだけでは非同期処理の待ち合わせはされるが、マルチスレッドにはならないということですかね。

UniTaskのasync/await

UniTask(UniRx.Async)でも同様になります。

public class TaskTest2 : MonoBehaviour
{
    private async void Start()
    {
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call Start");
        await TestAsync2();
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Start");
    }

    public async UniTask TestAsync2()
    {
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call TestAsync2");
        await Task.Delay(3000);
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End TestAsync2");
    }
}

 結果、すべて同じスレッドで実行されています。

f:id:ninagreen:20190203052246p:plain

Taskをマルチスレッドで実行

別のスレッドで実行させたい場合は、Task.Runが使えます(マルチスレッドで実行する方法は他にもあるようですが割愛します)

public class TaskTest3 : MonoBehaviour
{
    private async void Start()
    {
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call Start");
        await TestAsync();
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Start");
    }

    public async UniTask TestAsync()
    {
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Call TestAsync");

        await Task.Run(() =>
        {
            Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " Start Run");
            Thread.Sleep(3000);
            Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End Run");
        });
        
        Debug.Log("Id: " + Thread.CurrentThread.ManagedThreadId + " End TestAsync");
    }
}

このように書いて実行すると以下のように、Task.Runに渡したActionは別スレッドで実行されます。 

f:id:ninagreen:20190203053208p:plain

 

ただ、ゲームのUI処理やロジック処理でマルチスレッドを使う機会はほとんどないのでこれ以上は追わないことにします。

UniTaskの作者のこの意見には私も同意です。

UniTaskはどちらかというとJavaScript的(シングルスレッドのための非同期の入れ物)に近いです。Taskは、そうした非同期の入れ物に加えてマルチスレッドのためなどなど、とにかく色々なものが詰まりすぎていて、あまりよろしくはない。非同期とマルチスレッドは違います。明確に分けたほうが良いでしょうし、UnityではC# JobSystemを使ったほうが良いので、カジュアルな用途以外(まぁラクですからね)ではマルチスレッドとしてのTaskの出番は少なくなるでしょう。

neue.cc