---
# System prepended metadata

title: ROS spin notes

---

# ROS spin 詳細解說

ROS 裡面不只有 spin() & spinOnce() 可以使用，此篇對各種 spin 的 function 功能差異詳細解說
主要可以分成 Single-Thread(單執行緒) 與 Multi-Thread(多執行緒)
### Single-Thread
- ```ros::spin()```

    - 程式停在本行，在node被終止才會return。只有在呼叫到該API時才開始執行callback function

```cpp=
int main(int argc, char **argv){
    ros::init(argc, argv, "node_name");
    ros::NodeHandle n;
    ros::Subscriber sub = n.subscribe("topic", 1000, D);

    A();
    B(); //假設在執行其他行程式碼的時候收到/topic送來的message
    C();

    ros::spin();  //在呼叫這行的時候才會執行上面已經定義的callback function D()
    return 0;
}
```
Result :  ``` A B C D D D D .....```
- ```ros::spinOnce()```: 只執行一次，可避免 race condition, 也是在呼叫到該API時才執行callback function
- 需包在 ```while()``` 中使用
```cpp=
int main(int argc, char **argv){
    ros::init(argc, argv, "node_name");
    ros::NodeHandle n;
    ros::Subscriber sub = n.subscribe("topic", 1000, D);

    while(ros::ok()){    
        A();
        B();
        C();
        ros::spinOnce();
    }
    
    return 0;
}
```
Result: ``` A B C D A B C D .... ```

---

### Multi-Thread
- ```ros::MultiThreadSpinner()``` : 

    - 似 ```ros::spin()``` 會停在本行
    - 但 Single Thread 未必能保證時效性
    - ex: 若在其中一個 callback 中執行較久，可能會影響到其他 callback
    - Reference: https://zhuanlan.zhihu.com/p/375418691
```cpp=
#include <thread>
#include "ros/ros.h"
#include "std_msgs/String.h"
void CallbackA(const std_msgs::String::ConstPtr &msg)
{
    // let callback sleep (似 callback 中執行較多, operation 卡在 callback 中)
    std::this_thread::sleep_for(std::chrono::seconds(2));
    ROS_INFO(" I heard: [%s]", msg->data.c_str());
}

void CallbackB(const std_msgs::String::ConstPtr &msg)
{
    ROS_INFO(" I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
    ros::init(argc, argv, "listener");
    ros::NodeHandle n;
    ros::Subscriber sub_b = n.subscribe("B/message", 1, CallbackB);
    ros::Subscriber sub_a = n.subscribe("A/message", 1, CallbackA);
    
    // set number of threads
    ros::MultiThreadedSpinner spinner(2);
    spinner.spin();

    return 0;
}
 ```

Result: (可知 CallbackB 不會被 CallbackA 的 sleep 影響）
```
 [ INFO] [1621390946.100496300]:  I heard: [/B/message 1]
 [ INFO] [1621390947.100450900]:  I heard: [/B/message 2]
 [ INFO] [1621390948.015270200]:  I heard: [/A/message 1]
 [ INFO] [1621390948.099850900]:  I heard: [/B/message 3]
 [ INFO] [1621390949.100118400]:  I heard: [/B/message 4]
 [ INFO] [1621390950.015475900]:  I heard: [/A/message 3]
 [ INFO] [1621390950.100807500]:  I heard: [/B/message 5]
 [ INFO] [1621390951.100232500]:  I heard: [/B/message 6]
 [ INFO] [1621390952.015688200]:  I heard: [/A/message 5]
 ```
 
- ```ros::AsyncSpinner()```:
     
    - each callback will get its own thread (if available)
    - code will not be blocked 
    - Description:
        - ```ros::AsyncSpinner spinner(0);``` create spinner, and designate thread number (0: use as many threads as processors)
        - ```spinner.start();``` start the spinner (runs in background) and program continues
        - ```ros::waitForShutdown();``` AsyncSpinner won't block, will wait for the node to be killed
    
Example1: 
```cpp=
int main(int argc, char **argv)
{
    ros::init(argc, argv, "talker_subscribers");
    ros::NodeHandle nh;
    ros::AsyncSpinner spinner(0);
    spinner.start();
    ros::Subscriber counter1_sub = nh.subscribe("talker1", 10, callbackTalker1);
    ros::Subscriber counter2_sub = nh.subscribe("talker2", 10, callbackTalker2);
    ros::waitForShutdown();
    // ros::spin(); --> we don't use that anymore
}
```

Example 2:
```cpp=
#include "ros/ros.h"
#include "ros/callback_queue.h"
#include "std_msgs/Int64.h"

ros::CallbackQueue queue_1;
ros::CallbackQueue queue_2;

void callBack_1(const std_msgs::Int64::ConstPtr& msg){
	std::cout<<"1Get: "<<msg->data<<std::endl;
	ros::Duration(3).sleep();
}

void callBack_2(const std_msgs::Int64::ConstPtr& msg){
	std::cout<<"2Get: "<<msg->data<<std::endl;
}

void callBack_3(const std_msgs::Int64::ConstPtr& msg){
	std::cout<<"3Get: "<<msg->data<<std::endl;
}

int main(int argc, char** argv){
	ros::init(argc, argv, "MultiThread");
	ros::NodeHandle nh("~");
	nh.setCallbackQueue(&queue_1);
	ros::Subscriber sub_1 = nh.subscribe("Call1", 1, &callBack_1);
	ros::Subscriber sub_3 = nh.subscribe("Call3", 1, &callBack_3);

	nh.setCallbackQueue(&queue_2);
	ros::Subscriber sub_2 = nh.subscribe("Call2", 1, &callBack_2);

	std::cout<<"Running: "<<std::endl;

    ros::AsyncSpinner spinner_1(1, &queue_1);
	spinner_1.start();

	ros::AsyncSpinner spinner_2(1, &queue_2);
	spinner_2.start();

	ros::waitForShutdown();
}
```
>Note:
> - queue_1 負責 callBack_1 和 callBack_3
> - queue_2 負責 callBack_2
> - 如果把 ros::AsyncSpinner spinner_1(1, &queue_1); 中的 1 換成 2，代表使用兩個 thread 處理 callBack_1 和 callBack_3，就不會 block
--- 

講解不錯的 Reference: https://roboticsbackend.com/ros-asyncspinner-example/