在我的项目中,函数 clipsUpdate
读取一些由 CLIPS 设置的事实,而不受我的 C++ 代码的干扰。基于读取的事实,clipsUpdate 调用所需的函数。
void updateClips(void)
{
// read clipsAction
switch(clipsAction)
{
case ActMove:
goToPosition (0, 0, clipsActionArg);
break;
}
}
在goToPosition
函数中,向小车发送消息让小车移动到指定位置,然后使用while循环等待小车到达指定位置。
void goToPosition(float north, float east, float down)
{
// Prepare and send the message
do
{
// Read new location information.
}while(/*Specified position reached?*/)
}
问题是 updateClips
应该每 500 毫秒调用一次,当调用 goToPosition
函数时,执行会被阻塞,直到到达目标位置。在此等待期间,可能会发生需要车辆停下来的事情。因此,updateClips 无论如何都应该每 500 毫秒调用一次,并且如果 goToPosition
正在运行,它应该能够停止执行。
我试过如下使用线程,但它对我来说没有成功,而且我很难调试。我认为可以用更简单、更清洁的方式来完成。
case ActMove:
std::thread t1(goToPosition, 0, 0, clipsActionArg);
t1.detach();
break;
我的问题是,如何在不阻止执行的情况下检查是否已到达目标位置,即不使用 while
?
最佳答案
您可能需要一个事件驱动模型。
在事件驱动模型中,您的主引擎是一个紧密循环,它读取事件、更新状态,然后等待更多事件。
有些事件是基于时间的,有些是基于输入的。
唯一允许阻塞主线程的代码是主循环,它会阻塞直到计时器命中或新事件到达。
它可能非常粗略地看起来像这样:
using namespace std::literals::chrono_literals;
void main_loop( engine_state* state ) {
bool bContinue = true;
while(bContinue) {
update_ui(state);
while(bContinue && process_message(state, 10ms)) {
bContinue = update_state(state);
}
bContinue = update_state(state);
}
}
update_ui
如果需要,向用户提供反馈。
process_message(state, duration)
查找要处理的消息,或等待 10 毫秒。如果它看到一条消息(如 goToPosition
),它会修改 state 以反射(reflect)该消息(例如,它可能会存储所需的目的地)。它不会阻塞,也不会花费很多时间。
如果duration
中没有收到消息时间,它无论如何都会返回而不修改 state
(我假设即使没有新的输入/消息出现,您也希望事情发生)。
update_state
需要 state
并进化它。 state
可能有最后更新的时间戳; update_state
然后将使“物理”反射(reflect)自上次以来的时间。或者进行任何其他更新。
重点是process_message
不对状态起作用(它编码欲望),而update_state
推进“现实”。
它返回 false
如果主循环应该退出。
update_state
每 process_message
调用一次打电话。
updateClips
每 500 毫秒调用一次可以编码为消息队列中的重复自动事件 process_message
阅读。
void process_message( engine_state* state, std::chrono::milliseconds ms ) {
auto start = std::chrono::high_resolution_clock::now();
while (start + ms > std::chrono::high_resolution_clock::now()) {
// engine_state::delayed is a priority_queue of timestamp/action
// ordered by timestamp:
while (!state->delayed.empty()) {
auto stamp = state->delayed.front().stamp;
if (stamp >= std::chrono::high_resolution_clock::now()) {
auto f = state->queue.front().action;
state->queue.pop();
f(stamp, state);
} else {
break;
}
}
//engine_state.queue is std::queue<std::function<void(engine_state*)>>
if (!state->queue.empty()) {
auto f = state->queue.front();
state->queue.pop();
f(state);
}
}
}
重复轮询是作为延迟操作实现的,作为其第一个操作,在该操作之后插入一个新的延迟操作 500 毫秒。我们传入操作应运行的时间。
可以将“正常”事件插入正常操作 queue
, 这是 std::function<void(engine_state*)>
的序列并按顺序执行。
如果无事可做,上面的函数忙等待ms
时间然后返回。在某些情况下,我们可能想去 sleep 。
这只是一个事件循环的草图。互联网上有很多很多。
关于c++ - 定期检查条件而不阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45600460/