2014年3月7日金曜日

BackGroundWorkerを利用してKinectのイベントを別スレッドで実装する

~ 一部間違いがありましたので訂正します(2014/3/7) ~

Kinectのプログラミングが進んでくると、データ処理部分が肥大化してきます。それは仕方がないことですが、システム全体のパフォーマンスが落ちることになるので、マルチスレッドにより処理する必要が出てきます。この際のマルチスレッド化ですが、具体的には
  • Kinectから得たデータの処理を別スレッドに回す
  • KinectのReady系イベントを別スレッドに回す
という2つのアプローチがあります。前者はよくされていると思いますが、後者は意外に知られてないかもしれません。確かに、普通はKinectの各種イベントはFormスレッド内に記述することが多いと思いますが、UI周りの処理が複雑化するとシステム全体のパフォーマンスが落ちる可能性があるのでしょう。

BackgroudWorkerを利用してみる
某サイト(最下部参照)にそのことが書かれていたのですが、そのサイトはThreadクラスを使って検証していたようなので、今回はBackgroudWorkerとの合わせ技で別プロセスでの処理が可能かどうか検証してみました。BackgroundWorkerの利便性は前記事で述べたとおりです(C#:BackGroundWorkerコンポーネントを利用したマルチスレッド処理)。

で、結論から言えば成功です。実装のポイントは
  • DoWorkイベント内で初期設定からstart()までを記述する(EventHandlerの登録処理は必須)
  • スレッドを維持する必要はなし(ループ処理は不要)
  • UIコンポーネントへの操作も普通にできる(ProgressChangedイベントさえ不要)
  • ColorFrameReadyでのPictureBoxへの描画は普通に可能
という感じです。UIコンポーネントへの操作はいつものようにFrameReady系のメソッドで書けばいいので、従来との互換性も高いです。 

(訂正です)UIへの操作について、Labelの値を変えようとしたらエラーになってしまいました。すべてのUIが操作可能というわけではないようですね。とりあえずInvokerで操作はできます。

本当に別スレッドなのか?
しかし、本当に別スレッドになってるのかどうか、すこし心配になったので、
  • System.Threading.Thread.CurrentThread.ManagedThreadId
を各イベント内でコールして確認してみました。具体的には
  • Form_Load()
  • backgroundWorker1_DoWork()
  • kinect_ColorFrameReady()
  • kinect_SkeltonFrameReady()
  • Button_Click()
の4つのイベント系関数でコールして、従来の手法と今回の手法で、IDに違いがでてくるのかを確認しました。結論を言うと上記イベントでの各IDを横に並べてみると
  • 従来: 9,10,9,9,9
  • 今回:9,10,11,11,9
という感じになりました(数字はIDの例で、数字自体に意味はありません)。つまり、従来の方法だとFormのスレッドとKinectのスレッドは同じスレッドで動いていたことがわかります。今回の手法だと、Formのスレッドとは異なり、またBackgroundWorkerのスレッドとも異なるスレッドで処理されているようです。
このことから言えるのは、従来の方法だとFormの描画もUIのイベント(例えばボタンクリック)もKinectの処理もすべてシングルスレッドで処理していたことになりますので、本手法の効果は大きいと思います。

Ready系イベントをそれぞれ独立にスレッド化はできない
もしかして、Ready系の各イベントを別スレッド化するってこともできるんじゃないか?と思い、BackgroundWorkerを増やして試してましたが、残念ながらそれは無理でした。これらのイベントは1つのスレッドで処理されるようです。

以上、プログラムが肥大化した人は参考にしてください。

参考サイト
Kinect SDK C# FrameReady系イベントの実行スレッドについて



0 件のコメント:

コメントを投稿