MRが楽しい

MRやVRについて学習したことを書き残す

UnityでSynchronizationContextを使ってメインスレッドで処理を呼び出す

本日はUnityの小ネタ枠です。
UnityでSynchronizationContextを使ってメインスレッドで処理を呼び出す方法についてです。

SynchronizationContextを使ってメインスレッドで処理を呼び出す

SynchronizationContextはスレッドコンテキストを提供するクラスです。
UnityではメインスレッドでSynchronizationContext.Currentを使うことで、メインスレッドで処理を実行するためのオブジェクトを取得できます。
SendまたはPostメソッドを使って処理を受け渡すことができます。
learn.microsoft.com

// メインスレッドのコンテキストを取得
var mainThreadContext = System.Threading.SynchronizationContext.Current;

// Sendメソッドは同期的に処理を実行します
mainThreadContext.Send(new System.Threading.SendOrPostCallback((state) =>
{
    // スレッドに受け渡す処理
}), null);

// Postメソッドは非同期的に処理を実行します
mainThreadContext.Send(new System.Threading.SendOrPostCallback((state) =>
{
    // スレッドに受け渡す処理
}), null);

サンプルスクリプト

SendまたはPostメソッドを使ってメインスレッドに処理を受け渡す差プルスクリプトを作成しました。
・MainThreadTest2.cs

using UnityEngine;

/// <summary>
/// メインスレッドを判定するサンプルクラス
/// </summary>
public class MainThreadTest2 : MonoBehaviour
{
    // 現在のメインスレッドのID
    private int mainThreadId;

    // 現在のメインスレッドのコンテキスト
    private System.Threading.SynchronizationContext mainThreadContext;

    void Start()
    {
        // メインスレッドのIDを取得
        mainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;

        // メインスレッドのコンテキストを取得
        mainThreadContext = System.Threading.SynchronizationContext.Current;

        Debug.Log("Main Thread ID: " + mainThreadId);
        Debug.Log("Main Thread Context: " + (mainThreadContext != null ? "Available" : "Not Available"));
    }

    [ContextMenu("Send Method Test")]
    void SendMethodTest()
    {
        // バックグラウンドスレッドで確認
        new System.Threading.Thread(() =>
        {
            // バックグラウンドで実行されているかどうかを判定
            Debug.Log("Start Message : " + (IsMainThreadByThreadId(System.Threading.Thread.CurrentThread.ManagedThreadId) ? "Main Thread" : "Not Main Thread"));

            mainThreadContext.Send(new System.Threading.SendOrPostCallback((state) =>
            {
                // メインスレッドでの処理
                // メインスレッドかどうかを判定
                Debug.Log("Context Send Message : " + (IsMainThreadByThreadId(System.Threading.Thread.CurrentThread.ManagedThreadId) ? "Main Thread" : "Not Main Thread"));
            }), null);

            // バックグラウンドで実行されているかどうかを判定
            Debug.Log("End Message : " + (IsMainThreadByThreadId(System.Threading.Thread.CurrentThread.ManagedThreadId) ? "Main Thread" : "Not Main Thread"));
        }).Start();
    }

    [ContextMenu("Post Method Test")]
    void PostMethodTest()
    {
        // バックグラウンドスレッドで確認
        new System.Threading.Thread(() =>
        {
            // バックグラウンドで実行されているかどうかを判定
            Debug.Log("Start Message : " + (IsMainThreadByThreadId(System.Threading.Thread.CurrentThread.ManagedThreadId) ? "Main Thread" : "Not Main Thread"));

            mainThreadContext.Post(new System.Threading.SendOrPostCallback((state) =>
            {
                // メインスレッドでの処理
                // メインスレッドかどうかを判定
                Debug.Log("Context Post Message : " + (IsMainThreadByThreadId(System.Threading.Thread.CurrentThread.ManagedThreadId) ? "Main Thread" : "Not Main Thread"));
            }), null);

            // バックグラウンドで実行されているかどうかを判定
            Debug.Log("End Message : " + (IsMainThreadByThreadId(System.Threading.Thread.CurrentThread.ManagedThreadId) ? "Main Thread" : "Not Main Thread"));
        }).Start();
    }

    /// <summary>
    /// スレッドIDを使ってメインスレッドかどうかを判定するメソッド
    /// </summary>
    /// <param name="checkThreadId"></param>
    /// <returns></returns>
    private bool IsMainThreadByThreadId(int checkThreadId)
    {
        // 現在のスレッドがメインスレッドかどうかを判定
        return checkThreadId == mainThreadId;
    }
}

以下の通り、Sendメソッドでは同期的に処理が行われるため、バックグラウンド処理 -> メインスレッド処理 -> バックグラウンド処理の順でログが表示されます。

以下の通り、Postメソッドでは非同期的に処理が行われるため、バックグラウンド処理 -> バックグラウンド処理 -> メインスレッド処理の順でログが表示されます。