在看xsens的ROS驱动源码时,遇到了如下代码,其中涉及到了多线程里的wait_for函数:
// Returns empty packet on timeout
RosXsDataPacket XdaCallback::next(const std::chrono::milliseconds &timeout)
{
RosXsDataPacket packet;
std::unique_lock<std::mutex> lock(m_mutex);
if (m_condition.wait_for(lock, timeout, [&] { return !m_buffer.empty(); }))
{
assert(!m_buffer.empty());
packet = m_buffer.front();
m_buffer.pop_front();
}
return packet;
}
wait_for的官方参考资料:http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/
std::condition_variable::wait_for的原型有两种:
template <class Rep, class Period>
cv_status wait_for (unique_lock<mutex>& lck,
const chrono::duration<Rep,Period>& rel_time);
template <class Rep, class Period, class Predicate>
bool wait_for (unique_lock<mutex>& lck,
const chrono::duration<Rep,Period>& rel_time, Predicate pred);
我们主要看有谓词pred的版本。wait_for会阻塞所在线程(该线程应当拥有lck),直至超过了rel_time,或者谓词返回true。在阻塞的同时会自动调用lck.unlock()
让出线程控制权。对上述行为进行总结:
- 只要谓词返回true,立刻唤醒线程;
- 当谓词为false,没超时就继续阻塞,超时了就唤醒;
- 当其他线程使用
notify_one
或者notify_all
进行唤醒时,取决于谓词的状态,若为false,则为虚假唤起,线程依然阻塞。
回到开始的代码,这一段主要是为了取缓冲区里的数据。若m_buffer
里有数据(来自GPS/IMU),则直接返回栈顶,并弹出;若没有数据,则至多等待timeout
,超时则会因为assert而报错。
顺便补充下notify_one
或者notify_all
的区别:
这俩都是condition_variable的成员函数。当执行时,notify_one
会唤醒一个等待该变量的线程,但并不指定具体哪一个,notify_all
则会唤醒所有等待该变量的线程;如果没有在等待的线程,那就无事发生。