如何使用C++ 11 创建计时器事件

How to create timer events using C++ 11?

本文关键字:计时器 事件 创建 何使用 C++      更新时间:2023-10-16

如何使用C++ 11创建计时器事件?

我需要这样的话:"从现在起 1 秒后给我打电话"。

有图书馆吗?

做了一个简单的实现,我相信是你想要实现的。可以将类later与以下参数一起使用:

  • int(等待运行代码的毫秒)
  • bool(如果为 true,它会立即返回并在指定时间后在另一个线程上运行代码)
  • 变量参数(正是您要提供给 std::bind 的内容)

您可以将std::chrono::milliseconds更改为 std::chrono::nanosecondsmicroseconds 以获得更高的精度,并添加第二个 int 和 for 循环以指定运行代码的次数。

在这里,享受:

#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("%in", a);
    return;
}
int main()
{
    later later_test1(1000, false, &test1);
    later later_test2(1000, false, &test2, 101);
    return 0;
}

两秒后输出:

101

Edward 的异步解决方案:

  • 创建新线程
  • 睡在那个线程中
  • 在该线程中执行任务

很简单,可能只适合你。

我还想给出一个具有以下优点的更高级版本:

  • 无线程启动开销
  • 每个进程只需要一个额外的线程来处理所有定时任务

这在大型软件项目中可能特别有用,在这些项目中,您在流程中重复执行了许多任务,并且您关心资源使用情况(线程)和启动开销。

想法:有一个服务线程来处理所有注册的定时任务。为此使用增强io_service。

代码类似于: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 1sn"); });
  boost::asio::deadline_timer t2(io, boost::posix_time::seconds(1));
  t2.async_wait([](const boost::system::error_code& /*e*/){
    printf("Printed after 1sn"); });
  // 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;
}

使用 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;});

这是我到目前为止的代码:

我正在使用VC++ 2012(无可变参数模板)

//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;
}

如果您使用的是 Windows,则可以使用 CreateThreadpoolTimer 函数来计划回调,而无需担心线程管理,也无需阻塞当前线程。

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 callbackn";
    };
    call_after(callback, 1000);
    std::cin.get();
}

我正在寻找一个简单的解决方案,我发现的所有内容都太长太复杂了。阅读文档后,我发现只需几行代码即可完成此操作。

这个问题可能很老,但对未来的研究人员有益。

示例:如果要停止线程,请将isContinue设置为 false

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

根据您的情况使用detach()join()

  • 使用 detach() 时,执行主线程继续运行。
  • 使用 join() 时,执行主线程暂停并等待直到新线程结束。