Help us understand the problem. What is going on with this article?

HoloLensでUnity/UWPのライブラリ両方使う際のスレッド問題について

More than 3 years have passed since last update.

悩ましいスレッド問題

一般的なスレッド問題といえばデッドロック系の話、GUIを伴うアプリではメインスレッドとサブで使うスレッド間の問題。。。などなどあるかと思います。
ここ数か月HoloLensのサンプルアプリを開発していてこのスレッド周りが普段作っているアプリの中でもとびぬけて面倒というかわかりにくかったので備忘録として残しておきます。
HoloLensでUnity/UWP両方がっつり使うときには見ていただくと少しは幸せになるかもしれません。

HoloLensの開発で直面したもの

いくつか投稿しているサンプルの中で私の場合よくUWP側の実装を使います。理由がUnityわからないからというよこしまな部分もあるのですが、Microsoftの各サービスとの連携を考えると必然的にUWP寄りに実装するほうがシームレスです。
例えばCognitive Service APIなんかはもはやクラスのインスタンス化と1メソッド呼ぶだけで連携できてしまうくらいの手軽さです。

何が問題になるのか

例えばUnityでがっつり作っていてUWPはただビルドで吐き出して未変更でデプロイするなら問題になりにくいとおもいます。問題になるのはUnityとUWPの両方で実装するパターンです。
UnityにしてもUWPにしても、基本的なスレッドの考え方は一般的なスレッドの構成と同じだと思います。
つまりプロセスを起動すると実行される「メインスレッド」と「その都度呼び出されるスレッド」の2系統です。
このメインスレッドは使うアプリケーションのタイプによっていろいろ呼び方があると思うのですが、UWP等.NETでは画面が伴うアプリケーションでは「UIスレッド」という表現が多いかと思います。
image.png

HoloLensでUnityとUWP両方を使う場合ちょっとした問題が起こります。UnityはUnityで「Appスレッド」というUnity側でのメインスレッド(?)がありその上で処理が動いています。ですが、先に少し話したUWPなどの.NETの一部の機能は「UIスレッド」上での動作を要求します。つまりUWPの機能を利用するときはUIスレッドを使用しUnity側のライブラリであればAppスレッドを使用する必要が出てきます。図にするとこういう関係があることがわかります。

image.png

対応方法

この問題の解決策はそれほど難しいわけではなくAppCallBacksクラスを利用して適切なスレッドでアプリを動かせは解決は可能です。どう切り替えるかについては比較的わかりやすいです。ある程度対処療法的にもできるのですが以下の要素についてはまず間違いなくそれぞれのスレッドでの動作が必要になります。

  • 画面オブジェクトの操作(ex:表示テキストの変更、座標変更など)
  • リソースアクセス操作(ファイルI/Oなど)
  • デバイス関連(カメラなど)

上記の機能をUnityのライブラリでやればAppスレッド上で、UWPのライブラリでやればUIスレッドでとなります。

対処パターン

私がよく使っているパターンとしてUWP側でリソース系の処理を行った後にその結果をUnityの画面に出すというものです。この場合、UWP側なのでUIスレッドを使用します。UWP側の処理が終わってからAPPスレッドでUnityの画面の処理を呼ぶ実装を入れることが多いです。

// Copyright(c) 2017 Takahiro Miyaura
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php
AppCallbacks.Instance.InvokeOnUIThread(async () =>
{
    //UIスレッド(UWP)側での対処が必要な処理

    AppCallbacks.Instance.InvokeOnAppThread(() => {

        //Appスレッド(Unity)側での対処が必要な処理

    }, false);
}, true);

同期/非同期での問題

基本的にマルチスレッドを想定した処理を書くように気を付ければいいのですが、「HoloLensでCognitive Service APIを使ってみたー困ったことー」に記載したような予想しえない問題も時々出てきます。カメラからスクリーンショットをとるためのメソッドなのですが非同期メソッドのためawaitで待機させてもエラーになります。キャプチャデータをストリームに書き込んでいるんだと思うのですが。。。対処方法としてはawaitではなく同期するように処理を変更することで対処できました。

最後に

マルチスレッドになる状況での実装ではいろいろ気を配っておかないと思わぬ動作になります。それぞれ見極めが必要ですが基本的には「リソースなどの次の処理までに確実に終わってほしい処理」のみ同期処理を行い、それ以外は非同期とすれば
おおむね解決すると思います。その時に「この処理はUnity?UWP?」と問いかけてみて正しいスレッドで処理するように気を付ければよいと思います。

miyaura
C#ベース。現在はMR関連の技術に興味あり。 Microsoft MVP for Windows Development(2018-2019)
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした