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.
#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..