Taskを使用した非同期処理のテスト

この記事は2017年07月15日にqiitaに投稿した内容です。

環境

Unity2017.1.0f3

概要

.NET4.6が使用できるようになったのでTask・Async/Awaitのテスト

.NET4.6の設定

「PlayerSettings」>「OtherSettings」>「ScriptRuntimeVertion」を「Experimental(.NET4.6Equivalent)」に変更する

プログラム

順次実行と並列実行をしています

Assets/TestTask.cs

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

public class TestTask : MonoBehaviour
{
    [SerializeField] private Text _text = null;

    private IEnumerator Start()
    {
        yield return new WaitForSeconds( 3.0f );
        //順次実行
        yield return StartCoroutine( TestTaskSequentialProcess() );
        //並列実行
        yield return StartCoroutine( TestTaskParallelProcess() );
    }

    private IEnumerator TestTaskSequentialProcess()
    {
        Debug.Log( "StartTestTaskSequential:" + Thread.CurrentThread.ManagedThreadId + "--------------------" );
        var prevTime = Time.realtimeSinceStartup;
//      var task = Task.Run( () => TaskSequential() );
        var task = TaskSequential();

        //task.Wait();//awaitのせいでデッドロックする
        while( true )
        {
            if( task.IsCanceled == true || task.IsCompleted == true || task.IsFaulted == true )
                break;
            var time = Time.realtimeSinceStartup;
            Debug.Log( "DeltaTime:" + ( time - prevTime ).ToString() );
            prevTime = time;
            yield return null;
        }
        Debug.Log( "CompleteTestTaskSequential" );
        yield break;
    }

    private IEnumerator TestTaskParallelProcess()
    {
        Debug.Log( "StartTestTaskParallel:" + Thread.CurrentThread.ManagedThreadId + "--------------------" );
        var prevTime = Time.realtimeSinceStartup;
        //var task = Task.Run( () => TaskParallel() );
        var task = TaskParallel();
        //task.Wait(); //動作するが中身がわからないと使えないので使わない
        while( true )
        {
            if( task.IsCanceled == true || task.IsCompleted == true || task.IsFaulted == true )
                break;
            var time = Time.realtimeSinceStartup;
            Debug.Log( "DeltaTime:" + ( time - prevTime ).ToString() );
            prevTime = time;
            yield return null;
        }
        Debug.Log( "CompleteTestTaskParallel" );
        yield break;
    }

    //Task.Run()で呼び出されなければ、呼び出し元と同じスレッドで実行される
    //メインスレッドで実行されているならUnityの関数が使える
    //awaitで待っている間、Taskは中断され、他に処理がうつる
    private async Task TaskSequential()
    {
        Debug.Log( "StartTaskSequential:" + Thread.CurrentThread.ManagedThreadId );
        if( _text != null )
            _text.text = "StartTaskSequential";

        for ( var i = 0; i < 5; i++ )
        {
            var index = i;
            //awaitでTaskが終わるまで中断
            await Task.Run(() => Sleep( index ) );
        }
        Debug.Log( "CompleteTaskSequential" );
    }

    private Task TaskParallel()
    {
        Debug.Log( "StartTaskParallel:" + Thread.CurrentThread.ManagedThreadId );
        if( _text != null )
            _text.text = "StartTaskParallel";

        var tasks = new List<Task>();
        for ( var i = 0; i < 5; i++ )
        {
            var index = i;
            var task = Task.Run(() => Sleep( index ) );
            tasks.Add( task );
        }
        //すべてのタスクが完了した時に完了するタスクを生成
        var taskAll = Task.WhenAll( tasks );

        //この時点では終わっていない
        //ここで終わらせたければ asyncにしてawait Task.WhenAll()にする
        Debug.Log( "CompleteTaskParallel" ); 
        return taskAll;
    }

    private void Sleep( int index )
    {
        Debug.Log( "Sleep:" + Thread.CurrentThread.ManagedThreadId );
        Thread.Sleep( 1000 );
        Debug.Log( index );
    }
}

Threadを用いた並列処理

Task・Async/Awaitを使用したくない場合 http://qiita.com/fukaken5050/items/c6ea9233b708082ae1e9

参考

http://qiita.com/acple@github/items/8f63aacb13de9954c5da http://qiita.com/takutoy/items/6f3a20f7f48f36d918dd