おれんじりりぃぶろぐ

きっと何者にもなれないエンジニアのブログ

UnityにおけるAwaitAsyncを使った非同期実装について③

これまでTask、AsyncAwaitを使った非同期処理の実装、メインスレッド以外から特定の(メイン)スレッドにアクセスする方法についてみてきました。 今回は、複数の非同期に実行されるTaskをハンドリングする方法について説明します。今回もUnityに特化した情報はありません。

以下のコードがあったとします。

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour {

    private async void Start()
    {
        await TestAsyncMethod1();
        await TestAsyncMethod2();
        await TestAsyncMethod3();
        Debug.Log("All Compleated!");
    }

    private async Task TestAsyncMethod1()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 1");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod2()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 2");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod3()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 3");
            Thread.Sleep(1000); 
        });
    }
}

TestAsyncMethod1()が完了したらTestAsyncMethod2()が実行され、TestAsyncMethod2()が完了したらTestAsyncMethod3()が実行され、 TestAsyncMethod3()が完了したら最後にDebugログが出力されます。

図にすると以下のようになります。 スクリーンショット 2018-04-16 19.33.41.png (77.7 kB)

しかし、TestAsyncMethod1()TestAsyncMethod2()TestAsyncMethod3()に依存関係が特にない場合にはいちいちawait()するのは無駄です。 この場合、このようになってほしいわけです。 スクリーンショット 2018-04-16 19.46.45.png (76.1 kB)

それぞれのTaskをパラレルに実行し、全て完了したらDebugログを出力しています。

このような実装は以下のコードで実現できます。

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour {

    private async void Start()
    {
        List<Task> tasks = new List<Task>(); 
        tasks.Add(TestAsyncMethod1());
        tasks.Add(TestAsyncMethod2());
        tasks.Add(TestAsyncMethod3());

        await Task.WhenAll(tasks);
        
        Debug.Log("All Compleated!");
    }

    private async Task TestAsyncMethod1()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 1");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod2()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 2");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod3()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 3");
            Thread.Sleep(1000); 
        });
    }
}

tasksというTaskのコレクションを生成し、各々のtaskをaddしていきます。最後にTask.WhenAll()awaitよって全てのコレクション内のTaskが完了したタイミングを取得することができます。

参考