如何在c++中实现函数超时



我有函数f;我想在开始f之后抛出异常。我不能修改f()有可能在c++中实现吗?

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

您可以创建一个单独的线程来运行调用本身,并等待主线程中的条件变量,这将由执行f调用的线程在返回时发出信号。诀窍是用1秒的超时等待条件变量,这样,如果调用花费的时间超过超时,您仍然可以唤醒,知道它,并能够抛出异常—所有这些都在主线程中。下面是代码(现场演示在这里):

#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

您可以创建一个新线程并异步等待15秒通过,然后抛出异常。然而,异常只能在抛出它们的同一个线程中被捕获,因此,您不能在调用f()的同一个线程中捕获,就像在示例代码中一样-但这不是一个规定的要求,所以它可能对您来说是OK的。

只有保证f在小于15秒的时间内返回,才能同步执行此操作:

  • 存储当前时间
  • call 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;
}

相关内容

  • 没有找到相关文章

最新更新