我不完全确定如何最好地命名这个问题,因为我不完全确定问题的实质是什么(我猜“如何修复SegFault”不是一个好的标题)。
情况是,我写了这段代码:
template <typename T> class LatchedSubscriber {
private:
ros::Subscriber sub;
std::shared_ptr<T> last_received_msg;
std::shared_ptr<std::mutex> mutex;
int test;
void callback(T msg) {
std::shared_ptr<std::mutex> thread_local_mutex = mutex;
std::shared_ptr<T> thread_local_msg = last_received_msg;
if (!thread_local_mutex) {
ROS_INFO("Mutex pointer is null in callback");
}
if (!thread_local_msg) {
ROS_INFO("lrm: pointer is null in callback");
}
ROS_INFO("Test is %d", test);
std::lock_guard<std::mutex> guard(*thread_local_mutex);
*thread_local_msg = msg;
}
public:
LatchedSubscriber() {
last_received_msg = std::make_shared<T>();
mutex = std::make_shared<std::mutex>();
test = 42;
if (!mutex) {
ROS_INFO("Mutex pointer is null in constructor");
}
else {
ROS_INFO("Mutex pointer is not null in constructor");
}
}
void start(ros::NodeHandle &nh, const std::string &topic) {
sub = nh.subscribe(topic, 1000, &LatchedSubscriber<T>::callback, this);
}
T get_last_msg() {
std::lock_guard<std::mutex> guard(*mutex);
return *last_received_msg;
}
};
本质上,它所做的是订阅一个主题(通道),这意味着每次消息到达时都会调用一个回调函数。 这个类的任务是存储最后接收到的消息,这样类的用户总是可以访问它。
在构造函数中,我为消息和互斥体分配了一个shared_ptr来同步对此消息的访问。 这里使用堆内存的原因是,可以复制LatchedSubscriber
,并且仍然可以读取相同的锁存消息。 (subscriber
已经实现了这种行为,其中复制它不会做任何事情,只是一旦最后一个实例超出作用域就停止调用回调)。
问题基本上是代码分段出错。 我很肯定这是因为我的共享指针在回调函数中变成null
,尽管在构造函数中不是null。
ROS_INFO
调用print:
Mutex pointer is not null in constructor
Mutex pointer is null in callback
lrm: pointer is null in callback
Test is 42
我不明白怎么会这样。 我猜我不是误解了共享指针,ros主题订阅,就是两者兼而有之。
我做过的事:
这个
指针指定到另一个线程可能是不好的,因此我将其移到一个start
函数中,该函数在构造对象之后调用。shared_ptr
s的线程安全似乎有很多方面。 最初,我在回调中直接使用mutex
和last_received_msg
。 现在我已经将它们复制到局部变量中,希望这会有所帮助。 但这似乎没什么区别。
我想我已经把问题想通了。
订阅时,我将this
指针连同回调一起传递到subscribe函数。 如果复制了LatchedSubscriber
并且删除了原始文件,则该this
指针将无效,但sub
仍然存在,因此将继续调用回调。
我认为这种情况不会发生在代码中的任何地方,但LatcedSubscriber
作为成员存储在由唯一指针拥有的对象中。 看起来make_unique
可能正在内部进行某些复制? 在任何情况下,为回调使用this
指针都是错误的。
我最终做了以下事情
void start(ros::NodeHandle &nh, const std::string &topic) {
auto l_mutex = mutex;
auto l_last_received_msg = last_received_msg;
boost::function<void(const T)> callback =
[l_mutex, l_last_received_msg](const T msg) {
std::lock_guard<std::mutex> guard(*l_mutex);
*l_last_received_msg = msg;
};
sub = nh.subscribe<T>(topic, 1000, callback);
}
这样,两个智能指针的副本将与回调一起使用。
将闭包分配给boost::function
类型的变量似乎是必要的。 可能是因为subscribe
函数的方式。
这似乎解决了这个问题。 我还可能再次将订阅移到构造函数中,并删除start
方法。