我有以下问题: 我们有一个用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
时执行某些操作),这也是非忙等待的?
答案 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';
}
在工业质量解决方案中,您还需要一个中止标志或消息来停止循环。