避免繁忙等待和模式在实时和非实时线程之间切换

时间:2016-02-09 20:09:50

标签: c++ multithreading c++11 real-time

我有以下问题: 我们有一个用ros_control实现的控制器,它运行在Realtime,Xenomai linux补丁系统上。通过迭代调用更新函数来执行控制循环。我需要传达控制器的一些内部状态,为此,我使用在麻省理工学院开发的LCM。无论LCM的内部行为如何,发布方法都会破坏实时性,因此我在C ++ 11中实现了在分离线程上运行的发布循环。但是如果我不将辅助线程与控制器同步,它将以无限频率发布。因此,我也使用条件变量。

以下是控制器的示例:

MyClass mc;

// This is called just once
void init(){
    mc.init();
}

// Control loop function (e.g., called every 5 ms in RT)
void update(const ros::Time& time, const ros::Duration& period) {
    double value = time.toSec();
    mc.setValue(value);
}

对于试图发布的课程:

double myvalue;
std::mutex mutex;
std::condition_variable cond;
bool go = true;

void MyClass::init(){
    std::thread thread(&MyClass::body, this);
}


void MyClass::setValue(double value){
    myvalue = value;
    {
    std::lock_guard<std::mutex> lk(mutex);
    go = true;
    }
    cond.notify_one();
} 

void MyClass::body() {
    while(true) {
        std::unique_lock<std::mutex>lk(mutex);
        cond.wait(lk, [this] {return go;});    
        publish(myvalue); // the dangerous call
        go = false;
        lk.unlock();
    }
 }

此代码产生模式切换(即,实时断开)。可能是因为条件变量的锁定,我用它来同步辅助线程与主控制器,并与线程争用。如果我做这样的事情:

void MyClass::body() {
    while(true) {
        if(go){
        publish(myvalue);
        go = false;            
        }

    }
 }
 void MyClass::setValue(double value){
    myvalue = value;
    go = true;        
} 

我不会生成模式切换,但它也不安全,而且最重要的是我会忙着等待辅助线程。

有没有办法在主线程和辅助线程之间进行非阻塞同步(即body仅在调用setValue时执行某些操作),这也是非忙等待的?

2 个答案:

答案 0 :(得分:0)

使用无锁数据结构。

在这种情况下,您甚至不需要数据结构,只需使用go的原子。无需锁定。您可能会考虑使用信号量而不是条件变量来避免现在未使用的锁。如果你需要一个信号量来避免使用锁定,你最终会使用你的基本操作系统信号量,而不是C ++ 11,因为C ++ 11甚至没有它们。

答案 1 :(得分:0)

这并不完美,但它应该会减少你的繁忙等待频率,偶尔会失去响应能力。

这个想法是在通过原子传递消息时使用裸状态变量唤醒。

template<class T>
struct non_blocking_poke {
  std::atomic<T> message;
  std::atomic<bool> active;
  std::mutex m;
  std::condition_variable v;

  void poke(T t) {
    message = t;
    active = true;
    v.notify_one();
  }
  template<class Rep, class Period>
  T wait_for_poke(const std::chrono::duration<Rep, Period>& busy_time) {
    std::unique_lock<std::mutex> l(m);
    while( !v.wait_for(l, busy_time, [&]{ return active; } ))
    {}
    active = false;
    return message;
  }
};

等待线程每busy_time个醒来,看它是否错过了一条消息。但是,通常会获得比此更快的消息(存在错过消息的竞争条件)。此外,可以在没有救援人员获取的情况下发送多条消息。但是,如果发送了一条消息,则在大约1秒钟内,接收方将收到该消息或稍后的消息

non_blocking_poke<double> poker;

// in realtime thread:
poker.poke(3.14);

// in non-realtime thread:
while(true) {
  using namespace std::literals::chrono_literals;
  double d = poker.wait_for_poke( 1s );
  std::cout << d << '\n';
}

在工业质量解决方案中,您还需要一个中止标志或消息来停止循环。