调用超时方法

Che*_*hen 6 c++ c++14 c++17

我要调用的方法foo()超时(例如1分钟)。如果执行时间少于1分钟,则返回结果。否则将引发异常。这是代码:

//PRINT "START" IN THE LOG
auto m = std::make_shared<std::mutex>();
auto cv = std::make_shared<std::condition_variable>();
auto ready = std::make_shared<bool>(false);
auto response = std::make_shared<TResponse>();
auto exception = std::make_shared<FooException>();
exception->Code = ErrorCode::None;

std::thread([=]
{
    std::unique_lock<std::mutex> lk(*m);
    cv->wait(lk, [=]{ return *ready; });

    try
    {
        //PRINT "PROCESS" IN THE LOG
        auto r = foo();
        *response = std::move(r);
    }
    catch(const FooException& e)
    {
        *exception = std::move(e);
    }

    lk.unlock();
    cv->notify_one();
}).detach();

std::unique_lock<std::mutex> lk(*m);
*ready = true;
cv->notify_one();
auto status = cv->wait_for(lk, std::chrono::seconds(60));
if (status == std::cv_status::timeout)
{
    //PRINT "TIMEOUT" IN THE LOG
    //throw timeout exception
}
else
{
    //PRINT "FINISH" IN THE LOG
    if (exception->Code == ErrorCode::None)
    {
        return *response;
    }
    else
    {
        throw *exception;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到我在代码中添加了日志START / PROCESS / FINISH / TIMEOUT,每次执行此方法时,我都可以在日志中看到START / PROCESS / FINISH或START / PROCESS / TIMEOUT模式。但是,有时日志是START / PROCESS,没有任何完成/超时。我认为cv->wait_for最多应将当前线程阻塞60秒,然后它以TIMEOUT或FINISH存在。

foo()方法包含对网络驱动器的磁盘IO操作,该操作有时会挂起1多个小时(原因与该问题无关,现在无法解决),我尝试foo用线程睡眠代替,一切正常预期。该代码有什么问题,我该如何改进?

Mik*_*yke 4

由于调用中没有谓词cv->wait_for,因此线程可能会被虚假地解除阻塞。不过奇怪的是没有打印FINISH/TIMEOUT。因此,我们可能需要更多信息:该程序会发生什么?它是否挂起、是否抛出、是否直接退出、是否在之后的行中打印cv->wait_for

您可以尝试使用std::async并查看是否出现相同的行为(此外,它将大大简化您的代码):

std::future<int> res = std::async(foo);

std::future_status stat = res.wait_for(std::chrono::seconds(60));

if (stat != std::future_status::ready) {
  std::cout << "Timed out..." << "\n";
} else {
  try {
    int result = res.get();
    std::cout << "Result = " << result << std::endl;
  } catch (const FooException& e) {
    std::cerr << e.what() << '\n';
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑正如析构函数中块CuriouslyRecurringThoughts的未来的评论中所指出的。std::async如果这不是一个选项,则以下代码将使用一个std::promise和一个分离线程:

std::promise<int> prom;
std::future<int> res = prom.get_future();

std::thread([p = std::move(prom)]() mutable {
  try {
    p.set_value(foo());
  } catch (const std::exception& e) {
    p.set_exception(std::current_exception());
  }
}).detach();
Run Code Online (Sandbox Code Playgroud)

等待完成std::future如前所示。