繁体   English   中英

如何在 c++ 中实现 function 的超时

[英]How to implement timeout for function in c++

我有 function f; 我想在启动 f 后抛出异常 1s。 我无法修改 f()。 可以在 c++ 中做到吗?

try {
   f();
}
catch (TimeoutException& e) {
//timeout
}

您可以创建一个单独的线程来运行调用本身,并在主线程中等待一个条件变量,一旦它返回,调用f的线程就会发出信号。 诀窍是在 1s 超时的情况下等待条件变量,这样如果调用时间超过超时时间,您仍然会醒来,知道它,并能够抛出异常 - 全部在主线程中。 下面是代码(现场演示这里):

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std::chrono_literals;

int f()
{
    std::this_thread::sleep_for(10s); //change value here to less than 1 second to see Success
    return 1;
}

int f_wrapper()
{
    std::mutex m;
    std::condition_variable cv;
    int retValue;

    std::thread t([&cv, &retValue]() 
    {
        retValue = f();
        cv.notify_one();
    });

    t.detach();

    {
        std::unique_lock<std::mutex> l(m);
        if(cv.wait_for(l, 1s) == std::cv_status::timeout) 
            throw std::runtime_error("Timeout");
    }

    return retValue;    
}

int main()
{
    bool timedout = false;
    try {
        f_wrapper();
    }
    catch(std::runtime_error& e) {
        std::cout << e.what() << std::endl;
        timedout = true;
    }

    if(!timedout)
        std::cout << "Success" << std::endl;

    return 0;
}

您还可以使用std::packaged_task在另一个线程中运行您的函数 f() 。 这个解决方案或多或少类似于这个,只是它使用标准类来包装。

std::packaged_task<void()> task(f);
auto future = task.get_future();
std::thread thr(std::move(task));
if (future.wait_for(1s) != std::future_status::timeout)
{
   thr.join();
   future.get(); // this will propagate exception from f() if any
}
else
{
   thr.detach(); // we leave the thread still running
   throw std::runtime_error("Timeout");
}

您甚至可以尝试将其包装到函数模板中,以允许在超时时调用任意函数。 类似的东西:

template <typename TF, typename TDuration, class... TArgs>
std::result_of_t<TF&&(TArgs&&...)> run_with_timeout(TF&& f, TDuration timeout, TArgs&&... args)
{
    using R = std::result_of_t<TF&&(TArgs&&...)>;
    std::packaged_task<R(TArgs...)> task(f);
    auto future = task.get_future();
    std::thread thr(std::move(task), std::forward<TArgs>(args)...);
    if (future.wait_for(timeout) != std::future_status::timeout)
    {
       thr.join();
       return future.get(); // this will propagate exception from f() if any
    }
    else
    {
       thr.detach(); // we leave the thread still running
       throw std::runtime_error("Timeout");
    }
}

然后使用:

void f1() { ... }
call_with_timeout(f1, 5s);

void f2(int) { ... }
call_with_timeout(f2, 5s, 42);

int f3() { ... }
int result = call_with_timeout(f3, 5s);

这是一个在线示例: http : //cpp.sh/7jthw

可以新建一个线程,异步等待1s过去,然后抛出异常。 但是,异常只能在抛出它们的同一个线程中捕获,因此,您不能在调用f()的同一个线程中捕获异常,就像在示例代码中一样 - 但这不是规定的要求,因此可能是可以给你。

只有当f保证在 1s 内返回时,你才能同步执行此操作:

  • 存储当前时间
  • 调用f()
  • 等待当前时间 - 存储时间 + 1s

但要证明f实际上确实及时返回可能相当困难。

这建立在 Smeehee 的示例之上,如果您需要一个接受可变数量参数的参数(另请参见https://github.com/goblinhack/c-plus-plus-examples/blob/master/std_thread_timeout_template/README.md

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

int my_function_that_might_block(int x)
{
    std::this_thread::sleep_for(std::chrono::seconds(10));
    return 1;
}

template<typename ret, typename T, typename... Rest>
using fn = std::function<ret(T, Rest...)>;

template<typename ret, typename T, typename... Rest>
ret wrap_my_slow_function(fn<ret, T, Rest...> f, T t, Rest... rest)
{
    std::mutex my_mutex;
    std::condition_variable my_condition_var;
    ret result = 0;

    std::unique_lock<std::mutex> my_lock(my_mutex);

    //
    // Spawn a thread to call my_function_that_might_block(). 
    // Pass in the condition variables and result by reference.
    //
    std::thread my_thread([&]() 
    {
        result = f(t, rest...);
        // Unblocks one of the threads currently waiting for this condition.
        my_condition_var.notify_one();
    });

    //
    // Detaches the thread represented by the object from the calling 
    // thread, allowing them to execute independently from each other. B
    //
    my_thread.detach();

    if (my_condition_var.wait_for(my_lock, std::chrono::seconds(1)) == 
            std::cv_status::timeout)  {
        //
        // Throw an exception so the caller knows we failed
        //
        throw std::runtime_error("Timeout");
    }

    return result;    
}

int main()
{
    // Run a function that might block

    try {
        auto f1 = fn<int,int>(my_function_that_might_block);
        wrap_my_slow_function(f1, 42);
        //
        // Success, no timeout
        //
    } catch (std::runtime_error& e) {
        //
        // Do whatever you need here upon timeout failure
        //
        return 1;
    }

    return 0;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM