如何使用 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
How to create timer events using 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 later
with 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::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.
您可以更改std::chrono::milliseconds
为std::chrono::nanoseconds
或microseconds
以获得更高的精度,并添加第二个 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;
}