【ROS2 Foxy】TimerはReentrantにしても複数のインスタンスは生じないらしい
ROS 2 のexecutorの一種であるMultiThreadedExecutorは,複数のスレッドを用いて処理をパラレルに進めてくれます. 同時に進めて良い処理や同時に進めると不都合な処理をcallback groupを用いて設定します. Reentrant Callback Group に設定したコールバックは,制約なくスケジューリングされ,他のコールバックや,同じコールバックの別のインスタンスが同時に実行される可能性があると説明されています. しかし,実際には,create_wall_timer で設定したコールバックは Reentrant に設定しても,異なるインスタンスが同時に実行されることはないようです.
実験
ノードを用意し,1000 ms ごとにコールバックを呼び出すように設定します. コールバック関数の中で 1500 ms のスリープを設定し,呼び出された時刻とスリープが終わった時刻を記録します. 処理がパラレルに行われている場合,呼び出された時刻の記録は 1 秒おき,スリープが終わった時刻の記録も 1 秒おきになり,それぞれの間隔が 1.5 秒になるはずです. 主要な部分を下に示します. 全体はgithubにあります.
class TestNode : public rclcpp::Node {
public:
TestNode() : Node("test_node") {
group_ = nullptr;
group_ = this->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
timer_ = this->create_wall_timer(std::chrono::seconds(1),
std::bind(&TestNode::cb, this), group_);
}
private:
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::CallbackGroup::SharedPtr group_;
void cb() {
auto const t0 = this->get_clock()->now();
rclcpp::sleep_for(std::chrono::milliseconds(1500));
auto const t1 = this->get_clock()->now();
RCLCPP_INFO(this->get_logger(), "\nt0\t%ld\t%ld\nt1\t%ld\t%ld",
(long int)(t0.nanoseconds() * 1e-9),
t0.nanoseconds() % (long int)(1e9),
(long int)(t1.nanoseconds() * 1e-9),
t1.nanoseconds() % (long int)(1e9));
}
};
int main(int argc, char **argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<TestNode>();
rclcpp::executors::MultiThreadedExecutor executor;
executor.add_node(node);
RCLCPP_INFO(node->get_logger(), "num of threads: %d",
executor.get_number_of_threads());
executor.spin();
rclcpp::shutdown();
return 0;
}
結果と考察
実行すると以下の内容が表示されました.
[INFO] [1687669260.589471423] [test_node]: num of threads: 8
[INFO] [1687669263.090033840] [test_node]:
t0 1687669261 589953262
t1 1687669263 90031766
[INFO] [1687669264.590171461] [test_node]:
t0 1687669263 90109498
t1 1687669264 590169543
[INFO] [1687669266.090309286] [test_node]:
t0 1687669264 590247776
t1 1687669266 90307447
コールバックの処理が終了した直後に次の呼び出しが行われており,パラレルな処理は行われていないことがわかります. 最初の行によるとスレッド数は 8 で,十分な数のスレッドがあることが確認できます.
結論
タイトルの通り,Timer は Reentrant にしても複数のインスタンスは生じないようです. 1.5 秒かかる処理を 1 秒ごとに呼び出したいときには別の方法をとる必要がありそうです.