0
0

More than 1 year has passed since last update.

UniTaskを使えば並列処理で外部機器からデータを簡単に取得できますの紹介

Posted at

北陽の側域センサーを利用するにあたり、データ取得部分をUnityのMainThreadから切り離して取得することで、fpsを向上させるコードを書きました。外部装置と連携する際に、Threadingして並列処理でデータを取得したいケースはよくあるパターンかなと思いますが、UniTaskを利用すれば手軽に書くことができます。

その際に二点ほど気を付けるところがあります。

  1. 外から見えるデータを書き込むときはロックする
  2. ThreadのMain・ThreadPoolを切り替える

この2点を踏まえて、Sample Codeをみてください。

省略型のSample Code

using ... // 省略

public class Sensor : MonoBehaviour
{
    // 今回はセンサーで距離が取れる想定で、
    // このクラスの外側からはこのDistancesを使って距離情報にアクセスしてもらう
    public List<long> Distances => distances;
    private List<long> distances = new ();
    // その他は省略
    private object lookObj = new ();
    private CancellationTokenSource updateDataCanceler;

    void Start()
    {
        // 外部機器に接続するところ、場合によって異なるので無視して良い
        Open();

        updateDataCanceler = new CancellationTokenSource();
        // データ取得するためのスレッド開始、ここではまだMainThread
        UpdateDataOnThread().Forget();
    }

    private void OnDestroy()
    {
        // UnityEditorなどで停止するときに合わせてスレッドを停止させる
        updateDataCanceler.Cancel();

        // 外部機器との接続を閉じるところ、場合によって異なるので無視してよい
        Close();
    }

    // 外部機器との・・・略!
    private void Open() { ... }
    private void Close() { ... }
 
    // データ更新処理をループで回し続けるメソッド
    // cancelation tokenでキャンセルされまで実行し続ける
    private async UniTask UpdateDataOnThread()
    {
        // 注意①:下記章を参照
        while (!updateDataCanceler.IsCancellationRequested)
        {
            // 切り替え:この処理の後からUnityのメインスレッド外
            await UniTask.SwitchToThreadPool();

            // データ更新する実処理
            UpdateData();

            // 切り替え:この処理の後からUnityのメインスレッド内
            await UniTask.SwitchToMainThread();
        }
    }
    
    private List<long> temp = new ();
    private void UpdateData()
    {
        try
        {
            var data = urg.ReadLine();
            // まずはこのクラス内でしかアクセスできないtempを更新する
            if (!SCIP_Reader.MD(data, ref timeStamp, ref temp)) return;
            if (temp.Count == 0) return;

            // 外からdistancesがアクセスされている可能性があるのでlockして更新する
            lock (lookObj)
            {
                distances = temp.Select(t => t).ToList();
            }
        }
        catch (Exception ex)
        {
            Debug.Log(ex.Message);
        }
    }
}

注意①

ループ内でMainThreadとThreadPoolとのスイッチングをしないと、Unityの実行を止めたときに必ずUnityが固まってしまいました。おそらくUnityの停止処理からのキャンセルと、ThreadPool内の処理の連携がうまくいっていないと推測し、性能的に特に影響がなかったので、ループ内で都度Threadingを切り替えることで回避するようにしています。

前提

  • Unity 2021.3.11f1
  • UniTask 2.3.3
  • 北陽センサー UBG-04LX-F01

全コード・リポジトリ

0
0
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
0
0