如何在不使用睡眠/暂停代码的情况下延迟功能?

How could one delay a function without the use of sleep / suspending the code?

我需要将某个函数延迟 x 时间。问题是我不能使用睡眠,也不能使用任何暂停该功能的功能(那是因为该功能是一个包含更多功能的循环,睡眠/暂停一个将睡眠/暂停所有)

有什么办法可以做到吗?

如果你想在某个时间间隔执行一些特定的代码并且不想使用线程(能够暂停),那么你必须跟踪时间并在延迟时执行特定的代码超时了。

示例(伪):

timestamp = getTime();

while (true) {

    if (getTime() - timestamp > delay) {

        //main functionality

        //reset timer
        timestamp = getTime();

    }

    //the other functionality you mentioned

}

使用这种方法,您可以在 delay 指定的每个时间间隔调用一个特定的函数。其他函数将在循环的每次迭代中调用。

换句话说,延迟一个函数或在特定时间间隔执行它都没有区别。

假设您需要 运行 在具有自定义延迟的循环中使用自己的参数运行函数,并在每次迭代之前等待它们完成:

#include <cstdio>

void func_to_be_delayed(const int &idx = -1, const unsigned &ms = 0)
{
    printf("Delayed response[%d] by %d ms!\n", idx, ms);
}

#include <chrono>
#include <future>

template<typename T, typename ... Ta>
void delay(const unsigned &ms_delay, T &func, Ta ... args)
{
    std::chrono::time_point<std::chrono::high_resolution_clock> start = std::chrono::high_resolution_clock::now();
    double elapsed;
    do {
        std::chrono::time_point<std::chrono::high_resolution_clock> end = std::chrono::high_resolution_clock::now();
        elapsed = std::chrono::duration<double, std::milli>(end - start).count();
    } while(elapsed <= ms_delay);
    func(args...);
}

int main()
{
    func_to_be_delayed();
    const short iterations = 5;
    for (int i = iterations; i >= 0; --i)
    {
        auto i0 = std::async(std::launch::async, [i]{ delay((i+1)*1000, func_to_be_delayed, i, (i+1)*1000); } );
        // Will arrive with difference from previous
        auto i1 = std::async(std::launch::async, [i]{ delay(i*1000, func_to_be_delayed, i, i*1000); } );
        func_to_be_delayed();
        // Loop will wait for all calls
    }
}

注意:此方法可能会在每次调用 std::launch::async 类型的策略时产生额外的线程。

标准解决方案是实现事件循环。 如果你使用一些库、框架、系统API,那么很可能有类似的东西可以解决这类问题。

例如Qt有QApplication提供这个循环,还有QTimerboost::asioio_context 它提供了定时器可以是 运行 boost::asio::deadline_timer.

的偶数循环

您也可以尝试自己实现这样的事件循环。

提升示例:

#include <boost/asio.hpp>
#include <boost/date_time.hpp>
#include <exception>
#include <iostream>

void printTime(const std::string& label)
{
    auto timeLocal = boost::posix_time::second_clock::local_time();
    boost::posix_time::time_duration durObj = timeLocal.time_of_day();
    std::cout << label <<  " time = " << durObj << '\n';
}

int main() {
    boost::asio::io_context io_context;

    try {
        boost::asio::deadline_timer timer{io_context};
        timer.expires_from_now(boost::posix_time::seconds(5));

        timer.async_wait([](const boost::system::error_code& error){
            if (!error) {
                printTime("boom");
            } else {
                std::cerr << "Error: " << error << '\n';
            }
        });

        printTime("start");
        io_context.run();
    } catch (const std::exception& e) {
        std::cerr << e.what() << '\n';
    }
    return 0;
}

https://godbolt.org/z/nEbTvMhca

C++20 引入了 coroutines,这也是一个很好的解决方案。