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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14650885/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:33:40  来源:igfitidea点击:

How to create timer events using C++ 11?

c++c++11

提问by Thiago R. Adams

How to create timer events using C++ 11?

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

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

我需要这样的东西:“从现在起 1 秒后给我打电话”。

Is there any library?

有图书馆吗?

回答by Edward A

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

对我认为您想要实现的目标进行了简单的实现。您可以使用later带有以下参数的类:

  • 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)
  • int(等待运行代码的毫秒数)
  • bool(如果为 true,则立即返回并在指定时间后在另一个线程上运行代码)
  • 可变参数(正是您要提供给std::bind 的内容


You can change std::chrono::millisecondsto std::chrono::nanosecondsor microsecondsfor even higher precision and add a second int and a for loop to specify for how many times to run the code.

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

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

回答by Thiago R. Adams

This is the code I have so far:

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

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

我正在使用 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;
}

回答by tcb

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

如果你在 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 callback\n";
    };

    call_after(callback, 1000);
    std::cin.get();
}

回答by eci

The asynchronous solution from Edward:

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.

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

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

代码类似于: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;
}

回答by zwcloud

Use RxCpp,

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