首页 > 解决方案 > 在另一个线程中独立计时 For 循环的每个滴答声的最佳方法

问题描述

假设我有一个客户端线程和一个服务器线程。客户端线程必须执行昂贵的 for 循环操作,这很容易挂起。这样,服务器就可以独立判断for循环的每个tick是否超过了最大时间。这背后的背景是,如果完成一个滴答的时间太长,服务器将使客户端超时。

我最初的想法是在客户端和服务器线程中有两个 for 循环。服务器线程将有一个等待 1 秒的条件变量。如果客户端没有在每个tick的1秒内通知条件变量,服务器将超时:

服务器

 bool success;
 for (int i = 0; i < 10; i++) {
     std::unique_lock<std::mutex> lock(CLIENT_MUTEX);
     success = CLIENT_CV.wait_for(lock, std::chrono::seconds(1));
     if (!success) {
         std::cout << "timed out during tick " << i << std::endl;
         break;
     }
  }

客户

for (int i = 0; i < 10; i++) {
    std::unique_lock<std::mutex> lock(CLIENT_MUTEX);

    //do work

    CLIENT_CV.notify_one();
}

然而,我的实现尝试是不可靠的,并且在给客户相同的工作的情况下随机超时。如何改进设计以使其更可靠?

边注:

一个简单的解决方案是让服务器对整个 for 循环而不是每个滴答计时。但是,如果 for 循环在 10 次中的第 1 次失败,并且计时器正在等待 10 秒,则将在 10 秒后通知客户端。但是,如果服务器对每个滴答(10x1sec = 10 秒)施加 1 秒超时,则客户端将被通知超时,而无需等待整整 10 秒。

编辑。

整个客户端/服务器/超时的类比只是将问题置于上下文中。我纯粹对从不同线程计时 for 循环的最佳方法感兴趣。

标签: c++multithreadingtimeoutthread-safety

解决方案


这样做的一种方法可能是:

共享变量:

std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>> ledger;
std::mutex ledger_mtx;

客户:

for (int i = 0; i < 10; i++) {
    {
    std::scoped_lock lock(ledger_mtx);
    ledger.push_back(std::chrono::high_resolution_clock::now());
    }
    // Do work
}
{
std::scoped_lock lock(ledger_mtx);
ledger.push_back(std::chrono::high_resolution_clock::now());
}

服务器:

size_t id = 0;
std::this_thread::wait_for(1s); // Some time so that initial write to ledger is made
while(true) {
    {
    std::scoped_lock lock(ledger_mtx);
    if(ledger.size()==id) { /* Do something if the thread hangs */ }
    id = ledger.size();
    std::chrono::time_point<std::chrono::high_resolution_clock> last_tick = ledger.back();
    }
    if(id == 11) break;
    std::this_thread::sleep_for(1s - (std::chrono::high_resolution_clock::now() - last_tick));
}

这样您就可以对线程进行计时,同时从外部对其进行监控。这是最好的方法吗?可能不会,但它确实为您提供了所需的时间。


推荐阅读