dasukoの技術ブログ

現役エンジニアのブログです。

【Unity】メインスレッドで処理を実行する方法

はじめに

Unityではメインスレッドでしか実行できないAPIが多く存在します。(というかほとんどそう)

しかし、パフォーマンスを向上させるためには、マルチスレッドプログラミングが必須と言ってもいいでしょう。

マルチスレッドプログラミングについては以下の記事にまとめてあります。

dasuko.hatenadiary.jp

TaskやThreadでバックグラウンドスレッドで処理を実行した後に、

メインスレッドで処理を継続したい!ということがあるかと思います。

(UIの更新とか)

今回は特定のアクション(処理)をメインスレッドで実行する方法を紹介します。

メインスレッドで処理を実行する方法

UniRx

GitHub - neuecc/UniRx: Reactive Extensions for Unity

UniRxは.NET Reactive ExtensionsがiOS IL2CPPの互換性に問題があるために、Unity向けに再実装されたものです。

Reactive Extensionsの機能がメインですが、他にも便利な機能が多くあります。

メインスレッドに処理を委譲したい場合は、MainThreadDispatcherを使います。

以下にサンプルがあるので、確認してみてください!

UniRx/Sample10_MainThreadDispatcher.cs at master · neuecc/UniRx · GitHub

具体的な使い方としては、以下のように使います。

// background thread.

MainThreadDispatcher.Post(_ => Debug.Log("test"), null);

Debug.Log("test")の部分がメインスレッドで実行されます。

自作する

MainThreadDispatcherを実装するのは、そんなに難しいことではありません。

UnityのAPIのほとんどはメインスレッドでしか実行できず、

Unityのイベントもまたメインスレッドで実行されます。

つまり、メインスレッドで実行したい処理を変数に入れて、 Updateイベントで処理すれば、メインスレッドで実行されるということです。

よって、以下のように実装することができます。

(もちろん改善の余地はあります)

  
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;

public class MainThreadExecutor : MonoBehaviour
{
    private Queue<Action> actionQueue = new Queue<Action>();

    private static readonly object syncObject = new object();
    private static MainThreadExecutor instance;

    public static MainThreadExecutor Instance
    {
        get
        {
            lock (syncObject)
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<MainThreadExecutor>();
                }

                if (instance == null)
                {
                    instance = new GameObject(typeof(MainThreadExecutor).ToString()).AddComponent<MainThreadExecutor>();
                }
            }

            return instance;
        }
    }

    public static void Initialize()
    {
        var executor = MainThreadExecutor.Instance;
        Debug.Log("initialized :" + executor ?? executor.name);
    }

    public void Post(Action action)
    {
        lock (syncObject)
        {
            actionQueue.Enqueue(action);
        }
    }

    private void Update()
    {
        while (actionQueue.Any())
        {
            Action action = null;
            lock (syncObject)
            {
                action = actionQueue.Dequeue();
            }

            if (action != null)
            {
                action.Invoke();
            }
        }
    }
}

まとめ

今回はUnityで特定の処理をメインスレッドで実行するための方法をご紹介しました。

今回のサンプルはGithubで公開しておきました。

GitHub - dasuko10/MainThreadExecutor: Main Thread Executor for Unity

ぜひご意見ください!!!

参考

GitHub - neuecc/UniRx: Reactive Extensions for Unity