你能使用标准的c ++/c ++ 11实现一个没有"睡眠"的计时器吗?

cod*_*der 32 c++ timer c++11

我有以下代码(手工复制):

// Simple stop watch class basically takes "now" as the start time and 
// returns the diff when asked for.
class stop_watch {...}

// global var
std::thread timer_thread;

void start_timer(int timeout_ms)
{
    timer_thread = std::thread([timeout_ms, this](){
        stop_watch sw;
        while (sw.get_elapsed_time() < timeout_ms)
        {
            // Here is the sleep to stop from hammering a CPU
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }

        // Do timeout things here...
        std::cout << "timed out!" << std::endl;
    })
}
Run Code Online (Sandbox Code Playgroud)

我不想太沉迷于我写的课程的细节,所以这是一个非常简化的版本.完整的类调用函数回调并具有取消计时器等的变量...

我只想专注于"睡眠"部分.我可以在没有睡眠的情况下实现这样的事情,还是有更好的方法来实现它? - 还是睡得很好? - 我认为睡眠通常是糟糕设计的标志(我已经读过几个地方)......但我想不出一种方法来实现没有一个计时器:(

附加说明:计时器应该能够随时停止/唤醒.只是为了清晰起见而添加它,因为它似乎影响了要采用的解决方案.在我的原始代码(不是这个代码片段)中,我使用了一个可以突破循环的原子bool标志.

Seb*_*ian 34

C++ 11为我们提供了std::condition_variable.在您的计时器中,您可以等到满足您的条件:

// Somewhere else, e.g. in a header:
std::mutex mutex;
bool condition_to_be_met{false};
std::condition_variable cv;

// In your timer:
// ...
std::unique_lock<std::mutex> lock{mutex};
if(!cv.wait_for(lock, std::chrono::milliseconds{timeout_ms}, [this]{return condition_to_be_met;}))
std::cout << "timed out!" << std::endl;
Run Code Online (Sandbox Code Playgroud)

您可以在此处找到更多信息:https://en.cppreference.com/w/cpp/thread/condition_variable

要表明已满足条件,请在另一个线程中执行此操作:

{
    std::lock_guard<std::mutex> lock{mutex}; // Same instance as above!
    condition_to_be_met = true;
}
cv.notify_one();
Run Code Online (Sandbox Code Playgroud)

  • 只是一个小小的评论.`condition_to_be_met`的调整始终由互斥锁保护,因此它不需要是原子的.此外,几乎总是在调用`notify_one()之前释放锁.请参阅docs:https://en.cppreference.com/w/cpp/thread/condition_variable/notify_one (4认同)
  • @MarekR那不是真的,请阅读`std :: condition_variable`和`std :: unique_lock`的参考资料.基本上,`wait_for`可以在需要时锁定并解锁互斥锁.这就是为什么`wait_for`需要`std :: unique_lock`才能工作而不能使用`std :: lock_guard`. (4认同)

Dam*_*mon 6

虽然您的代码将"正常工作",但它对于作为计时器的预期目的而言是次优的.

存在std::this_thread::sleep_until,这取决于实现,可能只是刚刚呼吁sleep_for反正做一些数学后,但可能会使用适当的计时器,这是在精度和可靠性方面大大优于.

一般来说,睡觉并不是最好,最可靠,最准确的事情,但有时候,如果只是等待一段大概的时间就是预期的,它可以"足够好".

在任何情况下,如你的例子中反复睡眠少量是一个坏主意.这将在不必要的重新安排和唤醒线程上烧掉大量CPU,并且在某些系统上(特别是Windows,尽管Windows 10在这方面不再那么糟糕),它可能会增加相当多的抖动和不确定性.需要注意的是不同的Windows版本一轮调度的粒度不同,所以除了一般是不是过于精确,你甚至没有一致的行为.舍入几乎是"谁关心"单个大等待,但对于一系列小等待来说这是一个严重的问题.

除非过早地中止计时器的能力是必要的(但在这种情况下,还有更好的方法来实现它!),你应该在整个持续时间内完全一次,而不是更多.为了正确,你应该检查你确实得到了你预期的时间,因为有些系统(特别是POSIX)可能会睡眠不足.

过度睡眠是一个不同的问题,如果你需要它的权利,因为即使你检查并正确地检测这种情况下,一旦发生有什么可以做这件事(的时间已经过去了,一去不复返).但唉,这只是睡眠的根本弱点,你可以做的不多.幸运的是,大多数人可以在大多数情况下解决这个问题.