【问题标题】:timeout in std::asyncstd::async 中的超时
【发布时间】:2013-01-07 20:33:09
【问题描述】:

有没有办法在 std::async 方法中实现超时,所以如果线程在指定的时间内没有完成,我希望这个调用超时并完成。如何实现此功能。

【问题讨论】:

  • 将开始时间和最大持续时间传递给函数,让它在开始时(也可能在执行过程中)检查当前时间减去开始时间是否小于最大持续时间。
  • @GManNickG:如果存在任何包装器,我不想重写逻辑。
  • 没有,不过我还是给你写了一个。 :)

标签: c++ c++11 std stdthread stdasync


【解决方案1】:

没有(标准)方法可以进入线程并杀死它,无论如何这通常是个坏主意。更简洁的选择是将开始时间和最大持续时间传递给函数,然后(可能随着计算的进行多次)检查当前时间减去开始时间是否太长。

我会做这样的事情:

#include <chrono>

template <typename Clock = std::chrono::steady_clock>
class timeout
{
public:
    typedef Clock clock_type;
    typedef typename clock_type::time_point time_point;
    typedef typename clock_type::duration duration;

    explicit timeout(duration maxDuration) :
    mStartTime(clock_type::now()),
    mMaxDuration(maxDuration)
    {}

    time_point start_time() const
    {
        return mStartTime;
    }

    duration max_duration() const
    {
        return mMaxDuration;
    }

    bool is_expired() const
    {
        const auto endTime = clock_type::now();

        return (endTime - start_time()) > max_duration();
    }

    static timeout infinity()
    {
        return timeout(duration::max());
    }

private:
    time_point mStartTime;
    duration mMaxDuration;
};

这个简单的实用程序跟踪开始时间和最大持续时间(并提供一种指定无穷大的方法),并允许用户查询简单的事实,最重要的是是否发生超时。

下面测试;你可以通过定义/取消定义FAKE_DELAY来添加假延迟:

#include <iostream>
#include <future>

#define FAKE_DELAY

void fake_delay()
{
    #ifdef FAKE_DELAY
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    #endif
}

void short_running_function(timeout<> timelimit)
{
    fake_delay();

    if (timelimit.is_expired())
        std::cout << "short running thread ran out of time" << std::endl;
    else
        std::cout << "short running function finished" << std::endl;
}

void long_running_function(timeout<> timelimit)
{
    for (unsigned i = 0; i < 10; ++i) {
        if (timelimit.is_expired())
        {
            std::cout << "long running thread ran out of time" << std::endl;
            return;
        }

        std::cout << "long running thread doing work" << std::endl;
        fake_delay();
    }

    std::cout << "long running function finished" << std::endl;
}

int main()
{
    std::async(short_running_function,
               timeout<>(std::chrono::milliseconds(500))).wait();

    std::async(short_running_function,
               timeout<>(std::chrono::milliseconds(5000))).wait();

    std::async(long_running_function,
               timeout<>(std::chrono::milliseconds(500))).wait();

    std::async(long_running_function,
               timeout<>(std::chrono::milliseconds(5000))).wait();

    std::async(long_running_function,
               timeout<>::infinity()).wait();
}

FAKE_DELAYoff 的一种可能输出:

短跑功能完成
短跑功能完成
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行功能已完成
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行功能已完成
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行功能完成

FAKE_DELAYon 的一种可能输出:

短跑线程超时
短跑功能完成
长时间运行的线程正在工作
长时间运行的线程超时
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程超时
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行的线程正在工作
长时间运行功能完成

【讨论】:

  • 我想知道传递给 std::async 的方法是否可以说是一个阻塞调用(与您给出的示例中的 for 循环不同,它可以离散地检查过期),那么如何你执行超时吗?
  • @Adobri:你没有。该阻塞调用最终需要返回(或在其自身超时的情况下被调用)。
【解决方案2】:

该标准没有提供任何非合作终止线程的方法。缺少自动超时功能只是另一个例子。

相反,您必须实现您传递给std::async 的代码以协同处理此操作。协作杀死线程的一种模式是向函数传递一个对象,该对象提供了一种方法来检查异步函数是否应该继续,如果不继续则抛出异常。

struct Time_out {
    std::chrono::steady_clock start = std::chrono::steady_clock::now();
    std::chrono::milliseconds timeout;
    Time_out(std::chrono::milliseconds t) : timeout(t) {}

    void check() {
        if (start + timeout < std::chrono::steady_clock::now())
            throw timeout_exception();
    }
};

std::future<void> f = std::async([](Time_out t) {
    while(more work) {
        // do work
        t.check();
    }
}, Time_out(std::chrono::seconds(2));

f.get();

【讨论】:

  • @Ali 您可以随时检查以确保将延迟降至最低。没有其他选择,因为你不能只杀死线程(如果可以,你不应该这样做,因为它不安全)。
猜你喜欢
  • 1970-01-01
  • 2019-03-15
  • 2023-03-05
  • 2012-04-01
  • 1970-01-01
  • 2012-04-21
  • 1970-01-01
  • 2021-11-24
  • 2017-10-10
相关资源
最近更新 更多