簡體   English   中英

使用在不同線程中運行的成員回調 function 中的成員 shared_ptr(ROS 主題訂閱)

[英]Using member shared_ptr from a member callback function running in different thread (ROS topic subscription)

我不完全確定如何最好地命名這個問題,因為我不完全確定問題的本質是什么(我猜“如何修復段錯誤”不是一個好標題)。

情況是,我寫了這段代碼:

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;
  }
};

本質上它所做的是訂閱一個主題(頻道),這意味着每次消息到達時都會調用一個回調 function。 這個 class 的工作是存儲最后收到的消息,以便 class 的用戶可以隨時訪問它。

在構造函數中,我為消息分配了一個 shared_ptr,並為一個互斥鎖分配了對這個消息的同步訪問。 這里使用堆 memory 的原因是可以復制LatchedSubscriber並且仍然可以讀取相同的鎖存消息。 Subscriber已經實現了這種行為,復制它不會做任何事情,除了一旦最后一個實例超出范圍,回調就會停止調用)。

問題基本上是代碼段錯誤。 我很確定這是因為我的共享指針在回調 function 中變為null ,盡管在構造函數中不是 null。

ROS_INFO調用打印:

Mutex pointer is not null in constructor
Mutex pointer is null in callback
lrm: pointer is null in callback
Test is 42 

我不明白這怎么會發生。 我想我對共享指針、ros 主題訂閱或兩者都有誤解。

我做過的事情:

  1. 起初,我在構造函數中進行了訂閱調用。 我認為在構造函數返回之前將this指針指向另一個線程可能是不好的,所以我將它移到了一個start function 中,它在 object 構建后調用。
  2. shared_ptr的線程安全性似乎有很多方面。 起初我在回調中直接使用了mutexlast_received_msg 現在我已將它們復制到局部變量中,希望這會有所幫助。 但這似乎沒有什么不同。
  3. 我添加了一個本地 integer 變量。 我可以從回調中讀取我在構造函數中分配給這個變量的 integer。 只是一個健全性檢查,以確保回調實際上是在我的構造函數創建的實例上調用的。

我想我已經找到了問題所在。

訂閱時,我將this指針與回調一起傳遞給訂閱 function。 如果LatchedSubscriber曾經被復制並且原始的被刪除,那么this指針就會變得無效,但是sub仍然存在,所以回調會繼續被調用。

我不認為這發生在我的代碼中的任何地方,但LatcedSubscriber存儲為 object 中的成員,該成員由唯一指針擁有。 看起來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<void(const T)>類型的變量似乎是必要的。 可能是由於subscribe function 的方式。

這似乎已經解決了這個問題。 我可能還會將訂閱再次移入構造函數並擺脫start方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM