64

How to create timer events using C++ 11?

I need something like: “Call me after 1 second from now”.

Is there any library?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Thiago R. Adams
  • 835
  • 1
  • 7
  • 5
  • 1
    You should include any code that you have tried. – Miguel-F Feb 01 '13 at 16:57
  • 1
    See e.g. [this old answer](http://stackoverflow.com/a/11866539/440558) of mine. – Some programmer dude Feb 01 '13 at 17:01
  • 3
    There is a library: [boost.asio](http://www.boost.org/doc/libs/release/doc/html/boost_asio/tutorial/tuttimer2.html) – Cubbi Feb 01 '13 at 17:54
  • Thanks Joachim. That is I was looking for. Because I don't have access to the main loop (to check timers) I will have to create a thread. I will use a high resolution timer also. My sleep time will be very short (1 ms) and I hope not to use too much CPU. – Thiago R. Adams Feb 01 '13 at 20:58
  • What is the problem you are solving where this is your solution? – GManNickG Feb 01 '13 at 22:24
  • It's an animation and I want to use variable frame rate. The redraw frequency is proportional of speed of the object. Why not to use the maximum frequency ? To save CPU, batteries and keep the machine cold. it's not a game, sometimes the frequency is very low and sometimes can be high. 60fps – Thiago R. Adams Feb 02 '13 at 02:07
  • My higher frequency is 60 hertz. So the minimum timer is 1000/60 = 16.6ms. I can have approximately 1-8 simultaneous timers running. – Thiago R. Adams Feb 02 '13 at 02:18

6 Answers6

83

Made a simple implementation of what I believe to be what you want to achieve. You can use the class later with the following arguments:

  • int (milliseconds to wait until to run the code)
  • bool (if true it returns instantly and runs the code after specified time on another thread)
  • variable arguments (exactly what you'd feed to std::bind)

You can change std::chrono::milliseconds to std::chrono::nanoseconds or microseconds for even higher precision and add a second int and a for loop to specify for how many times to run the code.

Here you go, enjoy:

#include <functional>
#include <chrono>
#include <future>
#include <cstdio>

class later
{
public:
    template <class callable, class... arguments>
    later(int after, bool async, callable&& f, arguments&&... args)
    {
        std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));

        if (async)
        {
            std::thread([after, task]() {
                std::this_thread::sleep_for(std::chrono::milliseconds(after));
                task();
            }).detach();
        }
        else
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(after));
            task();
        }
    }

};

void test1(void)
{
    return;
}

void test2(int a)
{
    printf("%i\n", a);
    return;
}

int main()
{
    later later_test1(1000, false, &test1);
    later later_test2(1000, false, &test2, 101);

    return 0;
}

Outputs after two seconds:

101
Edward A
  • 2,291
  • 2
  • 18
  • 31
  • Very nice solution. But why not just a function instead a class? My concern about async is how fast it is to start. Having an exclusive thread running only for timers maybe it is faster. I will measure and then I post here again. – Thiago R. Adams Feb 02 '13 at 21:43
  • @Thiago R. Adams You're right, it can work as a function too, although class lets you add more functionality to it, as I said, this is just a mere example. You can also make a thread pool with as many threads as the number of cores (`std::thread::hardware_concurrency()`) and dispatch the timed events to it, etc. – Edward A Feb 03 '13 at 08:01
  • I did the first test. http://www.thradams.com/codeblog/timers.htm I sleep the thread calculating the remaning time to the next timer. If some timer is added I wake up the thread before. – Thiago R. Adams Feb 03 '13 at 22:11
  • 2
    Unfortunately your `std::async` call [won't do what you expect it to](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3451.pdf). Since you don't want any joining at all, you won't get around starting a detached `std::thread` yourself instead: `std::thread([after, task]() { std::this_thread::sleep_for(std::chrono::milliseconds(after)); task(); }).detach();`. – Christian Rau Feb 07 '13 at 09:02
  • Worked great for me. With a simple change to your code I was able to pass a chrono::system_clock::time_point instead of an int and call sleep_until. This allowed me to get the callback at a specific time. – Andrew Stern Dec 19 '13 at 21:35
  • This didn't work for me. Error 2 error C2011: '' : 'enum' type redefinition – jacobsgriffith Apr 30 '14 at 22:34
  • 11
    How will you cancel the timer? – Benji Mizrahi Mar 26 '15 at 09:39
  • @EdwardA Why is `f` an _rvalue_ (i.e. from the use of `&&`)? When you pass in the function `test1` or `test2`, aren't those implicitly _lvalues_? Just like how `x` in `int x = 5` is an _lvalue_, aren't `test1` and `test2 implicitly _lvalues_ too? – Code Doggo Feb 13 '20 at 19:32
9

The asynchronous solution from Edward:

  • create new thread
  • sleep in that thread
  • do the task in that thread

is simple and might just work for you.

I would also like to give a more advanced version which has these advantages:

  • no thread startup overhead
  • only a single extra thread per process required to handle all timed tasks

This might be in particular useful in large software projects where you have many task executed repetitively in your process and you care about resource usage (threads) and also startup overhead.

Idea: Have one service thread which processes all registered timed tasks. Use boost io_service for that.

Code similar to: http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/tutorial/tuttimer2/src.html

#include <cstdio>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

int main()
{
  boost::asio::io_service io;

  boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
  t.async_wait([](const boost::system::error_code& /*e*/){
    printf("Printed after 1s\n"); });

  boost::asio::deadline_timer t2(io, boost::posix_time::seconds(1));
  t2.async_wait([](const boost::system::error_code& /*e*/){
    printf("Printed after 1s\n"); });

  // both prints happen at the same time,
  // but only a single thread is used to handle both timed tasks
  // - namely the main thread calling io.run();

  io.run();

  return 0;
}
eci
  • 2,294
  • 20
  • 18
  • As I know, boost uses hardware timers. If I understand correctly, HPET generates interruption for each set timer what leads to thread context switching. From that, can we conclude that setting ~100k callback timers isn't suitable for high load application. Or I miss something? – slinkin Aug 10 '21 at 14:52
  • I like this answer for most situations. Boost::Asio is a library well worth getting acquainted with. – Rich von Lehe Jan 11 '22 at 18:31
7

Use RxCpp,

std::cout << "Waiting..." << std::endl;
auto values = rxcpp::observable<>::timer<>(std::chrono::seconds(1));
values.subscribe([](int v) {std::cout << "Called after 1s." << std::endl;});
zwcloud
  • 4,546
  • 3
  • 40
  • 69
  • This is the only one that works really well for me. I am using it with gRPC to stream responses unidirectionally and asynchronously. As of now there is no idiomatic way to do this. – errolflynn Mar 23 '20 at 17:21
5

This is the code I have so far:

I am using VC++ 2012 (no variadic templates)

//header
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <chrono>
#include <memory>
#include <algorithm>

template<class T>
class TimerThread
{
  typedef std::chrono::high_resolution_clock clock_t;

  struct TimerInfo
  {
    clock_t::time_point m_TimePoint;
    T m_User;

    template <class TArg1>
    TimerInfo(clock_t::time_point tp, TArg1 && arg1)
      : m_TimePoint(tp)
      , m_User(std::forward<TArg1>(arg1))
    {
    }

    template <class TArg1, class TArg2>
    TimerInfo(clock_t::time_point tp, TArg1 && arg1, TArg2 && arg2)
      : m_TimePoint(tp)
      , m_User(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2))
    {
    }
  };

  std::unique_ptr<std::thread> m_Thread;
  std::vector<TimerInfo>       m_Timers;
  std::mutex                   m_Mutex;
  std::condition_variable      m_Condition;
  bool                         m_Sort;
  bool                         m_Stop;

  void TimerLoop()
  {
    for (;;)
    {
      std::unique_lock<std::mutex>  lock(m_Mutex);

      while (!m_Stop && m_Timers.empty())
      {
        m_Condition.wait(lock);
      }

      if (m_Stop)
      {
        return;
      }

      if (m_Sort)
      {
        //Sort could be done at insert
        //but probabily this thread has time to do
        std::sort(m_Timers.begin(),
                  m_Timers.end(),
                  [](const TimerInfo & ti1, const TimerInfo & ti2)
        {
          return ti1.m_TimePoint > ti2.m_TimePoint;
        });
        m_Sort = false;
      }

      auto now = clock_t::now();
      auto expire = m_Timers.back().m_TimePoint;

      if (expire > now) //can I take a nap?
      {
        auto napTime = expire - now;
        m_Condition.wait_for(lock, napTime);

        //check again
        auto expire = m_Timers.back().m_TimePoint;
        auto now = clock_t::now();

        if (expire <= now)
        {
          TimerCall(m_Timers.back().m_User);
          m_Timers.pop_back();
        }
      }
      else
      {
        TimerCall(m_Timers.back().m_User);
        m_Timers.pop_back();
      }
    }
  }

  template<class T, class TArg1>
  friend void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1);

  template<class T, class TArg1, class TArg2>
  friend void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1, TArg2 && arg2);

public:
  TimerThread() : m_Stop(false), m_Sort(false)
  {
    m_Thread.reset(new std::thread(std::bind(&TimerThread::TimerLoop, this)));
  }

  ~TimerThread()
  {
    m_Stop = true;
    m_Condition.notify_all();
    m_Thread->join();
  }
};

template<class T, class TArg1>
void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1)
{
  {
    std::unique_lock<std::mutex> lock(timerThread.m_Mutex);
    timerThread.m_Timers.emplace_back(TimerThread<T>::TimerInfo(TimerThread<T>::clock_t::now() + std::chrono::milliseconds(ms),
                                      std::forward<TArg1>(arg1)));
    timerThread.m_Sort = true;
  }
  // wake up
  timerThread.m_Condition.notify_one();
}

template<class T, class TArg1, class TArg2>
void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1, TArg2 && arg2)
{
  {
    std::unique_lock<std::mutex> lock(timerThread.m_Mutex);
    timerThread.m_Timers.emplace_back(TimerThread<T>::TimerInfo(TimerThread<T>::clock_t::now() + std::chrono::milliseconds(ms),
                                      std::forward<TArg1>(arg1),
                                      std::forward<TArg2>(arg2)));
    timerThread.m_Sort = true;
  }
  // wake up
  timerThread.m_Condition.notify_one();
}

//sample
#include <iostream>
#include <string>

void TimerCall(int i)
{
  std::cout << i << std::endl;
}

int main()
{
  std::cout << "start" << std::endl;
  TimerThread<int> timers;

  CreateTimer(timers, 2000, 1);
  CreateTimer(timers, 5000, 2);
  CreateTimer(timers, 100, 3);

  std::this_thread::sleep_for(std::chrono::seconds(5));
  std::cout << "end" << std::endl;
}
Thiago R. Adams
  • 835
  • 1
  • 7
  • 5
5

If you are on Windows, you can use the CreateThreadpoolTimer function to schedule a callback without needing to worry about thread management and without blocking the current thread.

template<typename T>
static void __stdcall timer_fired(PTP_CALLBACK_INSTANCE, PVOID context, PTP_TIMER timer)
{
    CloseThreadpoolTimer(timer);
    std::unique_ptr<T> callable(reinterpret_cast<T*>(context));
    (*callable)();
}

template <typename T>
void call_after(T callable, long long delayInMs)
{
    auto state = std::make_unique<T>(std::move(callable));
    auto timer = CreateThreadpoolTimer(timer_fired<T>, state.get(), nullptr);
    if (!timer)
    {
        throw std::runtime_error("Timer");
    }

    ULARGE_INTEGER due;
    due.QuadPart = static_cast<ULONGLONG>(-(delayInMs * 10000LL));

    FILETIME ft;
    ft.dwHighDateTime = due.HighPart;
    ft.dwLowDateTime = due.LowPart;

    SetThreadpoolTimer(timer, &ft, 0 /*msPeriod*/, 0 /*msWindowLength*/);
    state.release();
}

int main()
{
    auto callback = []
    {
        std::cout << "in callback\n";
    };

    call_after(callback, 1000);
    std::cin.get();
}
tcb
  • 4,408
  • 5
  • 34
  • 51
4

I'm looking for a simple solution and everything I found is too long and complicated. After reading the documentation, I found that this can be done in just a few lines of code.

This question may be old but can beneficial to future researchers.

Example: Set isContinue to false if you want to stop the thread.

#include <chrono>
#include <thread>

volatile bool isContinue = true;

void NameOfYourFunction(){
  while(continue){
     std::this_thread::sleep_for(std::chrono::milliseconds(1000)); //sleep for 1 seconds
     //do something here after every 1 seconds...
  }
}

int main(){
  std::thread your_thread(NameOfYourFunction); // Register your `YourFunction`.
  your_thread.detach(); // this will be non-blocking thread.
  //your_thread.join(); // this will be blocking thread.
}

use detach() or join() depending on your situation.

  • When using detach(), the execution main thread continues running.
  • When using join(), the execution main thread pauses and waits until the new thread ends.
Polar
  • 3,327
  • 4
  • 42
  • 77