Is it correct to assume that a new thread will be used for every instance of every subscription and service callback?
Originally posted by bhaskara on ROS Answers with karma: 1479 on 2011-03-23
Post score: 11
Is it correct to assume that a new thread will be used for every instance of every subscription and service callback?
Originally posted by bhaskara on ROS Answers with karma: 1479 on 2011-03-23
Post score: 11
For a particular topic, there is only one thread that all the subscribers share.
Each service callback does get its own thread because there can only be one service callback for a particular service.
Future versions of rospy will likely have a different, more versatile threading model for subscriptions.
Originally posted by kwc with karma: 12244 on 2011-03-24
This answer was ACCEPTED on the original site
Post score: 8
Comment by tfoote on 2011-03-26:
What @kwc is describing is the callbacks.
Comment by kwc on 2011-03-24:
Yes, there is only one thread for a subscription, which includes the work of doing all the callbacks.
Comment by Patrick Bouffard on 2011-03-24:
Thanks. What about the callbacks? I had the impression that those were executed serially from a single queue for all subscribers.
Comment by kwc on 2011-03-24:
For each subscribed topic in a node, there is a thread. 5 subscribed topics, 5 threads. If you have two subscriptions to one topic, one thread. Publishers are synchronous/blocking, so the publishing occurs in the same thread. (the i/o engine of rospy is overdue for a rewrite)
Comment by Patrick Bouffard on 2011-03-24:
Ken, did you mean 'for a particular node ( or maybe nodehandle?), there is only one thread...'? If I have a python node that subscribes to 5 topics how many threads are in play? What about on the publishing node's side?
Comment by kwc on 2011-03-24:
Each service has a single, dedicated thread that handles the underlying transport and calling into the callback. Service requests are processed serially in the order they come off the socket.
Comment by bhaskara on 2011-03-24:
OK. For services, does it use a threadpool or spin up a new thread on each request?
Comment by Seanny123 on 2013-12-09:
The detailed replies have been great, but just to eliminate all ambiguity, from what I understand this also means that my script will wait for my callback to end before starting another callback, even if a subscription releases a new message. Is this correct?
Comment by paulbovbel on 2016-09-15:
Just to clarify, after finding something suprising - while one thread will serially run all subscription callbacks, services get a separate thread for each connection (https://github.com/ros/ros_comm/blob/kinetic-devel/clients/rospy/src/rospy/impl/tcpros_service.py#L247), thus one per client.
The accepted answer is not correct, at least with Kinetic. Consider the following:
#!/usr/bin/env python
import threading
import rospy
from std_msgs.msg import Empty
global i
i = 0
def callback(_):
global i
i += 1
j = i
while not rospy.is_shutdown():
print('sleeping: %d, thread: %s' % (j, threading.current_thread()))
rospy.Rate(1).sleep()
print('exiting loop for %d: %s' % (j, rospy.is_shutdown()))
def main():
rospy.init_node('foooo')
sub = rospy.Subscriber('foo', Empty, callback, queue_size=1)
if __name__ == '__main__':
main()
rospy.spin()
Then, call rostopic twice by running the following command twice in parallel in different shell sessions:
rostopic pub /foo std_msgs/Empty "{}"
Observe the following output:
sleeping: 1, thread: <Thread(/foo, started daemon 140177158719232)>
sleeping: 2, thread: <Thread(/foo, started daemon 140177150326528)>
sleeping: 1, thread: <Thread(/foo, started daemon 140177158719232)>
sleeping: 2, thread: <Thread(/foo, started daemon 140177150326528)>
sleeping: 1, thread: <Thread(/foo, started daemon 140177158719232)>
sleeping: 2, thread: <Thread(/foo, started daemon 140177150326528)>
sleeping: 1, thread: <Thread(/foo, started daemon 140177158719232)>
sleeping: 2, thread: <Thread(/foo, started daemon 140177150326528)>
sleeping: 1, thread: <Thread(/foo, started daemon 140177158719232)>
sleeping: 2, thread: <Thread(/foo, started daemon 140177150326528)>
sleeping: 1, thread: <Thread(/foo, started daemon 140177158719232)>
sleeping: 2, thread: <Thread(/foo, started daemon 140177150326528)>
Originally posted by rsinnet with karma: 101 on 2018-01-25
This answer was NOT ACCEPTED on the original site
Post score: 10
Comment by [email protected] on 2018-07-13:
You are right.
Comment by pitosalas on 2023-02-05:
I don't quite understand why the observed output demonstrates that the accepted answer is incorrect. Of course time may have passed and those two comments are out of sync.
It actually seems like a subscriber process has one thread per topic subscribed per publisher. So if node A subscribes to topic /foo
and node B publishes to topic /foo
and node C also publishes to topic /foo
, node A will have 2 callback threads.
ros_thread_sub.py
#!/usr/bin/env python
import threading
import rospy
from std_msgs.msg import String
def callback(msg):
print("Msg from %s on thread %s" % (msg.data, threading.current_thread()))
def main():
rospy.init_node('foooo')
rospy.Subscriber('foo', String, callback, queue_size=1)
rospy.spin()
if __name__ == '__main__':
main()
ros_thread_pub.py (starting publisher B and C in separate terminals)
#!/usr/bin/env python
import threading
import sys
import rospy
from std_msgs.msg import String
global proc_id
proc_id = ""
def publisher():
msg = String(proc_id)
r = rospy.Rate(1)
pub = rospy.Publisher('foo', String, queue_size=1)
while not rospy.is_shutdown():
print("publishing")
pub.publish(msg)
r.sleep()
def main():
global proc_id
proc_id = sys.argv[1]
rospy.init_node(proc_id)
thread = threading.Thread(target=publisher)
thread.start()
rospy.spin()
if __name__ == '__main__':
main()
Console output:
Msg from B on thread <Thread(/foo, started daemon 140237958694656)>
Msg from B on thread <Thread(/foo, started daemon 140237958694656)>
Msg from B on thread <Thread(/foo, started daemon 140237958694656)>
Msg from B on thread <Thread(/foo, started daemon 140237958694656)>
Msg from C on thread <Thread(/foo, started daemon 140237950301952)>
Msg from B on thread <Thread(/foo, started daemon 140237958694656)>
Msg from C on thread <Thread(/foo, started daemon 140237950301952)>
Msg from B on thread <Thread(/foo, started daemon 140237958694656)>
Msg from C on thread <Thread(/foo, started daemon 140237950301952)>
Msg from B on thread <Thread(/foo, started daemon 140237958694656)>
Originally posted by zacwitte with karma: 170 on 2018-04-18
This answer was NOT ACCEPTED on the original site
Post score: 8