Home > Software engineering >  How to run function after delay asynchronously in C
How to run function after delay asynchronously in C

Time:07-27

I want to implement something like Java's TimerTask in C . I want to use it for invoking functions sometimes, not periodic. For periodic launching it will be a good idea to implement "event loop" scheme with 2 threads, with creating tasks in the first thread and process it in the second. But I do not want to write much code. So I've written smth like this:

template <typename F, typename... Args>
auto timed_run(const uint64_t delay_ms, const F& function, Args... args) {
  const auto f = [&] {
    std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
    function(args...);
  };

  auto future = std::async(std::launch::async, f);
  return future;
}

But it does not work as I need because it is not asynchronous at all due to it waits at future destructor as described there.

So we need to create thread by ourselves. Okay, lets do it:

template <typename F, typename... Args>
auto timed_run(const uint64_t delay_ms, const F& function, Args... args) {
  std::packaged_task<void()> task([&]() {
    std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
    function(args...);
  });

  auto future = task.get_future();
  std::thread thread(std::move(task));
  thread.detach();

  return future;
}

In this implementation the are no locks and waits, but it simply does not run our function. It is because we can't use sleep on the detached threads.

So, how can I implement what i want?

CodePudding user response:

template <typename F, typename... Args>
auto timed_run(const uint64_t delay_ms, const F& function, Args... args) {
  std::packaged_task<void()> task([=]() {
    std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
    function(args...);
  });

  auto future = task.get_future();
  std::thread(std::move(task)).detach();

  return future;
}

CodePudding user response:

You can have your timed_run function launch an async task and return a future. At the callee point, just wait for the async task to complete.

[Demo]

#include <chrono>
#include <cstdint>  // uint64_t
#include <fmt/core.h>
#include <future>  // async
#include <thread>  // this_thread

template <typename F, typename... Args>
auto timed_run(const std::uint64_t delay_ms, F&& f, Args&&... args) {
    return std::async(std::launch::async, [=](){
        std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
        f(args...);
    });
}

int main() {
    using namespace std::chrono_literals;
    auto print_dots = []() {
        for (int i{4}; i > 0; --i) {
            fmt::print(".\n");
            std::this_thread::sleep_for(10ms);
        }
    };
    auto print_square = [](int n) { fmt::print("{}\n", n*n); };
    auto f1{ timed_run(0, print_dots) };
    auto f2{ timed_run(20, print_square, 2) };
    f1.get();
    f2.get();
}

// Outputs something like:
//
//   ..4..
  • Related