赞
踩
本指南旨在警告用户与Python 同步服务客户端 call()
API相关的风险。在同步调用服务时,很容易错误地导致死锁,因此我们不建议使用call()
。
官方虽然提供了demo,但是还是不推荐使用,建议避免同步调用,所以本指南还将介绍推荐的替代方法异步调用(call_async()
)的功能和用法。
C++服务调用API仅在异步中可用,因此本指南中的比较和示例与Python服务和客户端有关。
同步客户端会在发送请求直到收到响应这段时间完全阻塞调用call的线程,这个线程基本上在调用时属于独占了。调用儿断时间不确定,但是调用完成立马就返回了。
下面这个例子就是同步调用的,可以参看之前的教程
import sys from threading import Thread from example_interfaces.srv import AddTwoInts import rclpy from rclpy.node import Node class MinimalClientSync(Node): def __init__(self): super().__init__('minimal_client_sync') self.cli = self.create_client(AddTwoInts, 'add_two_ints') while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...') self.req = AddTwoInts.Request() def send_request(self): self.req.a = int(sys.argv[1]) self.req.b = int(sys.argv[2]) return self.cli.call(self.req) # This only works because rclpy.spin() is called in a separate thread below. # Another configuration, like spinning later in main() or calling this method from a timer callback, would result in a deadlock. def main(): rclpy.init() minimal_client = MinimalClientSync() spin_thread = Thread(target=rclpy.spin, args=(minimal_client,)) spin_thread.start() response = minimal_client.send_request() minimal_client.get_logger().info( 'Result of add_two_ints: for %d + %d = %d' % (minimal_client.req.a, minimal_client.req.b, response.sum)) minimal_client.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
这里面spin()
函数需要放到单独的线程来执行,因为call()
会阻塞主线程。
产生死锁的方式有下面两种(这么难用还是不要用了,用异步吧):
spin()
send_request()
位于回调中def trigger_request(msg):
response = minimal_client.send_request() # This will cause deadlock
minimal_client.get_logger().info(
'Result of add_two_ints: for %d + %d = %d' %
(minimal_client.req.a, minimal_client.req.b, response.sum))
subscription = minimal_client.create_subscription(String, 'trigger', trigger_request, 10)
rclpy.spin(minimal_client)
死锁很难定位,也不报错,很烦,最好就是不用这种方式
异步调用就是相当安全了,不会存在阻塞ros进程或者非ros进程的风险,异步调用完成之后,会立马返回future
,只需要等待就行,直接看看几种实现吧
import sys from example_interfaces.srv import AddTwoInts import rclpy from rclpy.node import Node class MinimalClientAsync(Node): def __init__(self): super().__init__('minimal_client_async') self.cli = self.create_client(AddTwoInts, 'add_two_ints') while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...') self.req = AddTwoInts.Request() def send_request(self): self.req.a = int(sys.argv[1]) self.req.b = int(sys.argv[2]) self.future = self.cli.call_async(self.req) def main(args=None): rclpy.init(args=args) minimal_client = MinimalClientAsync() minimal_client.send_request() while rclpy.ok(): rclpy.spin_once(minimal_client) if minimal_client.future.done(): try: response = minimal_client.future.result() except Exception as e: minimal_client.get_logger().info( 'Service call failed %r' % (e,)) else: minimal_client.get_logger().info( 'Result of add_two_ints: for %d + %d = %d' % (minimal_client.req.a, minimal_client.req.b, response.sum)) break minimal_client.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
这个是判断future
是否done
def main(args=None): rclpy.init(args=args) node = rclpy.create_node('minimal_client') # Node's default callback group is mutually exclusive. This would prevent the client response # from being processed until the timer callback finished, but the timer callback in this # example is waiting for the client response cb_group = ReentrantCallbackGroup() cli = node.create_client(AddTwoInts, 'add_two_ints', callback_group=cb_group) did_run = False did_get_result = False async def call_service(): nonlocal cli, node, did_run, did_get_result did_run = True try: req = AddTwoInts.Request() req.a = 41 req.b = 1 future = cli.call_async(req) try: result = await future except Exception as e: node.get_logger().info('Service call failed %r' % (e,)) else: node.get_logger().info( 'Result of add_two_ints: for %d + %d = %d' % (req.a, req.b, result.sum)) finally: did_get_result = True while not cli.wait_for_service(timeout_sec=1.0): node.get_logger().info('service not available, waiting again...') timer = node.create_timer(0.5, call_service, callback_group=cb_group) while rclpy.ok() and not did_run: rclpy.spin_once(node)
这种是await,python的语法了
别用同步回调,会死锁了,看看异步多好用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。