警告
您正在阅读的 ROS 2 文档版本已达到 EOL(生命周期结束),不再受官方支持。如果您想了解最新信息,请访问 Jazzy.
同步与异步服务客户端
水平: 中级
时间 10 分钟
导言
本指南旨在提醒用户注意与 Python 同步服务客户端相关的风险。 调用()
API。在同步调用服务时,很容易错误地造成死锁,因此我们不建议使用 调用()
.
我们提供了一个示例,说明如何使用 调用()
对于希望使用同步调用并意识到其中隐患的资深用户来说,这是正确的。我们还强调了可能出现的死锁情况。
由于我们建议避免使用同步调用,本指南还将介绍推荐的替代方法--异步调用(async calls)--的功能和使用方法。call_async()
).
C++ 服务调用 API 仅在 async 中可用,因此本指南中的比较和示例与 Python 服务和客户端有关。这里给出的 async 定义一般适用于 C++,但也有一些例外。
1 同步呼叫
同步客户端在向服务发送请求时会阻塞调用线程,直到收到响应为止;在调用期间,该线程上不会发生任何其他事情。调用可以花费任意多的时间来完成。一旦完成,响应将直接返回客户端。
下面是一个示例,说明如何正确执行同步客户端节点,该节点类似于 简单的服务和客户端 教程。
舶来品 系统
从 线程 舶来品 主题
从 example_interfaces.srv 舶来品 添加两个字符
舶来品 rclpy
从 rclpy.node 舶来品 节点
类 最小客户端同步(节点):
捍卫 启动(自我):
棒极了().启动('minimal_client_sync';)
自我.挛 = 自我.创建客户端(添加两个字符, 'add_two_ints')
虽然 不 自我.挛.等待服务(超时秒数=1.0):
自我.get_logger().信息('服务不可用,请再次等待...';)
自我.要求 = 添加两个字符.要求()
捍卫 发送请求(自我):
自我.要求.a = int(系统.参数[1])
自我.要求.b = int(系统.参数[2])
返回 自我.挛.致电(自我.要求)
# 这是因为 rclpy.spin() 是在下面的单独线程中调用的。
# 另一种配置,如在 main() 中稍后旋转或从定时器回调中调用此方法,会导致死锁。
捍卫 主要():
rclpy.启动()
最小客户端 = 最小客户端同步()
自旋线程 = 主题(目标=rclpy.后旋, 参数=(最小客户端,))
自旋线程.启动()
回应 = 最小客户端.发送请求()
最小客户端.get_logger().信息(
'add_two_ints 的结果: for %d + %d = %d' %
(最小客户端.要求.a, 最小客户端.要求.b, 回应.数额))
最小客户端.destroy_node()
rclpy.关闭()
如果 姓名____ == '__main__';:
主要()
内附说明 main()
客户端调用 rclpy.spin
在另一个主题中。两个 发送请求
和 rclpy.spin
是阻塞的,因此需要使用不同的线程。
1.1 同步死锁
同步 调用()
API 可能会导致死锁。
正如上述示例的注释中提到的,如果没有创建一个单独的线程来旋转 rclpy
是造成死锁的原因之一。当客户端阻塞一个线程等待响应,但响应只能在同一线程中返回时,客户端将永远无法停止等待,其他事情也无法发生。
造成僵局的另一个原因是阻塞 rclpy.spin
在订阅、定时器回调或服务回调中同步调用服务。例如,如果同步客户端的 发送请求
被置于回调中:
捍卫 触发请求(信息):
回应 = 最小客户端.发送请求() # 这将导致死锁
最小客户端.get_logger().信息(
'add_two_ints 的结果: for %d + %d = %d' %
(最小客户端.要求.a, 最小客户端.要求.b, 回应.数额))
订阅费 = 最小客户端.创建订阅(字符串, '触发器';, 触发请求, 10)
rclpy.后旋(最小客户端)
出现僵局的原因是 rclpy.spin
不会抢先使用 发送请求
调用。一般来说,回调只应执行轻快的操作。
警告
发生死锁时,您不会收到任何服务被阻塞的提示。不会有任何警告或异常抛出,堆栈跟踪中也不会有任何指示,调用也不会失败。
2 异步调用
中的异步调用 rclpy
是一种完全安全的服务调用方法,也是推荐的调用方法。与同步调用不同的是,它们可以在任何地方进行,而不会有阻塞其他 ROS 和非 ROS 进程的风险。
异步客户端将立即返回 未来
的值,表示在向服务发送请求后,调用和响应是否已完成(而不是响应本身的值)。返回的 未来
可随时查询以获得回复。
由于发送请求不会阻止任何事情,因此可以使用循环来旋转 rclpy
并检查 未来
例如,在同一主题中:
虽然 rclpy.好的():
rclpy.自旋一次(网站)
如果 未来.完成的():
#Get response
"(《世界人权宣言》) 简单的服务和客户端 Python 教程说明了如何执行异步服务调用并检索 未来
使用一个循环。
"(《世界人权宣言》) 未来
也可以使用定时器或回调来检索,例如在 本例或通过其他方法。作为调用者,您可以自行决定如何存储 未来
检查其状态,并检索您的回复。
摘要
不建议实施同步服务客户端。它们容易发生死锁,但在发生死锁时不会提供任何问题指示。如果您必须使用同步调用,请参阅本节中的示例。 1 同步呼叫 是一种安全的方法。您还应该了解第 1.1 同步死锁.我们建议使用异步服务客户端。