1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unityで重い処理は別スレッドで動かしたいけど(非同期処理)

Posted at

Unityで、起動時にフリーズした様な動作となるのが嫌で、非同期処理とマルチスレッドとを調べました。
Unity API を使う部分は、メインスレッドに入れなければならないことに注意し、非同期処理・マルチスレッドを実施すればよいことが分かりました。

詳しくは、以下の二つのサイトに書かれています。(じゃあ、この記事は何なんだよ!)

ということで、忘備録ですw

最初に困ったコード

    async void Start()
    {
        Debug.Log("Start: " + Thread.CurrentThread.ManagedThreadId);

        Debug.Log("Heavy Method: " + Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(5000);

        Debug.Log("Unity API Method: " + Thread.CurrentThread.ManagedThreadId);
        transform.position = new Vector3(0, 0, 0);

        Debug.Log("Start end: " + Thread.CurrentThread.ManagedThreadId);
    }

Thread.Sleep(5000)の行でフリーズした様になります。
メインスレッドを阻害しているということです。

Unity API コード部分があかんやつ

Task.Runを使って、全部マルチスレッドで処理しようとすると

    async void Start()
    {
        Debug.Log("Start: " + Thread.CurrentThread.ManagedThreadId);
        await Task.Run(() => HeavyMethod());
        Debug.Log("Start end: " + Thread.CurrentThread.ManagedThreadId);
    }
    
    void HeavyMethod()
    {
        Debug.Log("Heavy Method: " + Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(5000);

        Debug.Log("Unity API Method: " + Thread.CurrentThread.ManagedThreadId);
        transform.position = new Vector3(0, 0, 0);
    }

以下のようなエラーが出ます。

UnityException: get_transform can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

訳)
UnityException: get_transform はメインスレッドからのみ呼び出すことができます。
シーンをロードする際、コンストラクタとフィールド初期化子はロードスレッドから実行されます。
この関数はコンストラクタやフィールド初期化子では使用せず、代わりに初期化コードを Awake 関数または Start 関数に移動してください。

これが、Unity API が使えないというやつです。

結局こうするのね、というコード

Unity API を使う部分はマルチスレッドから出すしかありません。

    async void Start()
    {
        Debug.Log("Start: " + Thread.CurrentThread.ManagedThreadId);
        
        await Task.Run(() => HeavyMethod());
        
        Debug.Log("Unity API Method: " + Thread.CurrentThread.ManagedThreadId);
        transform.position = new Vector3(0, 0, 0);
        
        Debug.Log("Start end: " + Thread.CurrentThread.ManagedThreadId);
    }
    
    void HeavyMethod()
    {
        Debug.Log("Heavy Method: " + Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(5000);
    }

注意として、Startがasyncとなっているため、Start処理が終了する前にUpdateが始まります。

image.png

Start処理の中にUpdate内で使用するものを処理している場合、Start処理が完了していることを確認するコードが追加で必要です。

まとめ

非同期処理(asyncawait)とマルチスレッド(Task)を使うことで、重い処理を別スレッドで動かせることが分かりました。
また、Task.Run()で動かす処理の結果を待たなくていい場合は、非同期処理(asyncawait)はいらないです。

ちなみに、Task.Run()で動かしても、以下の様に戻り値も帰ってきます。

    async void Start()
    {
        Debug.Log("Start: " + Thread.CurrentThread.ManagedThreadId);
        
        int x = await Task.Run(() => HeavyMethod());
        Debug.Log(x);
        
        Debug.Log("Unity API Method: " + Thread.CurrentThread.ManagedThreadId);
        transform.position = new Vector3(0, 0, 0);
        
        Debug.Log("Start end: " + Thread.CurrentThread.ManagedThreadId);
    }
    
    int HeavyMethod()
    {
        Debug.Log("Heavy Method: " + Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(5000);

        return 1;
    }
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?