ROS(Python)でマルチプロセスを使ったらハマりました。。。

なんとビックリ、rospy.loginfoでプログラムが固まるのです!しかも、コード単体では期待通りに動くのに、別のROSノードを立ち上げると固まる謎現象… その状況で5,6回プログラムを再起動すると1回は想定通りに動く謎(。´・ω・)?

マルチプロセスでハマったとあるので想像は着くと思いますが、rospyが裏側でやっているlockの問題でした。(rospyの問題というと語弊があって、そもそもrospyはマルチプロセスで使うことを推奨していない)

どんなコードだったか

  • 画像をmsgとしてsubscribeして、ディープラーニングを通した結果をpublishするros node
  • ディープが重いので、その処理をしている間に届くメッセージが損失していたので、ディープの箇所を切り出して別プロセスで動かした
    • いろいろな理由で別プロセスにするしかなかった
  • スレッドにするとPythonのGlobal Interpreter Lockで結局1cpuしか使われなくて意味がない…
    • rospyはマルチプロセスが非推奨だが、マルチスレッドは推奨されている
  • ディープの結果をキューにためて、別スレッドでそのキューからgetしてpublishをしている
    • ここは今回あまり関係ない

といった感じで、ROSのノード内でマルチプロセスをしていました。

実際のコードは以下のような感じ

def main() -> None:
    """メイン関数"""
    detector = Detector() #コンストラクタでinit_nodeしてる
    Process(target=detector.run).start() 
    Thread(target=detector.publish).start() 
    rospy.spin()

このコードで、別プロセスにしているrunメソッドの中でrospy.loginfoをしているのですが、なぜかその個所で止まったり動いたり。。。

原因

原因は、Pythonのマルチプロセス、マルチスレッドに関する一般的にハマる話でした。スレッドとプロセスを同時に使っている場合に起こる問題です。今回の場合は、rospy.loginfoが内部でスレッドで獲得しようとしているlockができずにずっと待機している状態でした。

なぜこれが起こるかです。Processの開始時に、デフォルトでは親プロセスを引き継ぐようになっています。(forkで開始)そのため、あるスレッドがリソースに対してlockしていた時にプロセスが立ち上がるとlockされた状態のままプロセスが起動します。すると、立ち上がった別プロセスにコピーされたlock状態のlockは永遠に解放されません。スレッドは元のプロセス(親プロセス)のlockを開放しに行きますが、子プロセスとなっているlockは開放しません。すると、子プロセス内のスレッドがlockをずっと待っている状態になります。

今回の場合だと、runメソッドが別プロセスで起動したとき、rospyが内部でいろいろ立ち上げているサービスや他のノードとの競合が起きて、立ち上がった別プロセスでは永遠にlock待ちになったのです。rospy.loginfoで止まっていたので、別のノードでrospy.loginfo(あるいはC++でROS_INFO)が呼ばれていると、roscoreにlockを取りに行っているようなのですが、そのlockが子プロセスでlock状態のままになっていてrunメソッドのrospy.loginfoで止まっていたわけです。

そのため、タイミングによっては動いたり動かなかったりしていたのです!

終わりに

今回の話は、ROSというよりもPythonのマルチプロセス、マルチスレッドでハマる問題です。プロセスとスレッドを同時に使っていると、結構起こるようでPython3.12からはプロセスとスレッドが同時に使用されているとWarningが出るようになったようです。

また、「Processがforkで開始される」という話がありましたが、spawnで開始すれば今回の問題も起こらないはずです。ただし、親から引き継がずに1から立ち上げるのでプロセスの起動が遅くなるというデメリットも。このあたりの話は、公式ドキュメントに書いてあります。(https://docs.python.org/ja/3/library/multiprocessing.html#contexts-and-start-methods

ただし、ROSという観点で言えば、他にもいろいろやっているのでspawnにしても別の問題が起こる可能性があります。マルチプロセスにしている箇所からrosのコードを排除できれば起こらないはず?

マルチプロセス、マルチスレッド、いつもハマるから怖いよ(´;ω;`)

Categories:

Tags:

No responses yet

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA