我想在 C++ 中实现一些系统,以便我可以调用一个函数并要求在 X 毫秒内调用另一个函数。像这样的东西:
callfunctiontimed(25, funcName);
25 是调用函数之前的毫秒数。
我想知道这是否需要多线程,然后使用一些延迟功能?除了使用函数指针之外,这样的功能如何工作?
我想在 C++ 中实现一些系统,以便我可以调用一个函数并要求在 X 毫秒内调用另一个函数。像这样的东西:
callfunctiontimed(25, funcName);
25 是调用函数之前的毫秒数。
我想知道这是否需要多线程,然后使用一些延迟功能?除了使用函数指针之外,这样的功能如何工作?
对于便携式解决方案,您可以使用 boost::asio。下面是我前段时间写的一个demo。你可以改变
t.expires_from_now(boost::posix_time::seconds(1));
为了适合您,您需要在 200 毫秒后进行函数调用。
t.expires_from_now(boost::posix_time::milliseconds(200));
下面是一个完整的工作示例。它在重复调用,但我认为只需稍作更改就可以轻松调用一次。
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace std;
class Deadline
{
public:
Deadline(deadline_timer &timer) : t(timer) {
wait();
}
void timeout(const boost::system::error_code &e) {
if (e)
return;
cout << "tick" << endl;
wait();
}
void cancel() {
t.cancel();
}
private:
void wait() {
t.expires_from_now(boost::posix_time::seconds(1)); //repeat rate here
t.async_wait(boost::bind(&Deadline::timeout, this, boost::asio::placeholders::error));
}
deadline_timer &t;
};
class CancelDeadline {
public:
CancelDeadline(Deadline &d) :dl(d) { }
void operator()() {
string cancel;
cin >> cancel;
dl.cancel();
return;
}
private:
Deadline &dl;
};
int main()
{
io_service io;
deadline_timer t(io);
Deadline d(t);
CancelDeadline cd(d);
boost::thread thr1(cd);
io.run();
return 0;
}
//result:
//it keeps printing tick every second until you enter cancel and enter in the console
tick
tick
tick
您是否希望它异步执行,以便在 25 毫秒结束时执行回调而不阻塞主执行线程?如果是这样,您可以在与您实现的计时器/定时回调函数不同的线程中执行回调。
如果你不使用多线程,那么你的 main 或者 callfunctiontimed(25, funcName); 的调用函数;当你运行睡眠/睡眠时会阻塞。现在,您可以选择要实现的行为。
真正的解决方案不会像多线程那样简单。有一些事情,考虑到函数可以使用不同的超时和函数多次调用,你如何保留不同的计时器/回调信息。
一种方法是这样的:
当计时器线程完成睡眠时,它会删除并查看列表的头部并在新线程中执行函数指针。定时器线程在列表的新头部使用睡眠时间重新初始化。
main() {
//spawn a timer thread with pthread create
callfunctiontimed(25, test);
callfunctiontimed(35, show);
callfunctiontimed(4, print);
}
callfunctionTImed(int time, (func*)function, void*data) //
{
//add information to sorted list of timer and callbacks
//re-initialize sleep_time for timer thread if needed.
return.
}
timerThread() {
while(1){
sleep(sleep_time);
//look at head of timer list, remove it, call function in a new thread
//adjust sleep time as per new head
}
}
希望这能说明我的意思,尽管这并不完美并且有几个问题。
许多人在这里就此事提供了很好的答案,但我将直接解决这个问题,因为几年前我也遇到过类似的问题。我不能使用 Boost 有几个原因——我知道 Boost 在很多开源软件中都有很好的用途。此外,我真的很想了解计时器和回调,因为它与基于 Linux 的环境有关。所以,我自己写了。
从根本上说,我有一个Timer
班级和一个TimerCallback
班级。典型的回调,实现为类的继承类TimerCallback
,将回调时要执行的操作放在triggered ()
方法中,专门针对需要实现。
根据通常的语义,一个Timer
对象与一个回调对象相关联,该对象可能包含执行回调所需的所有信息。计时器调度由一个环境范围的计时器 minheap 管理,该计时器必须在单独的线程/进程中维护。这个 minheap 任务只做一件事:它对将来设置的回调事件的 minheap 进行 minheap。minheap 选择下一个要触发的事件,O(1)
并且可以将剩余的事件 minheapifyO(log n)
用于n
计时器事件。它还可以在(在此处O(log n)
阅读对堆的简要介绍)中插入新的计时器事件。
当定时器触发时,minheap 调度程序检查它是周期性定时器、单次定时器还是将执行特定次数的定时器。因此,计时器对象要么从 minheap 中删除,要么在下一个执行时间重新插入 minheap。如果要删除计时器对象,则将其从 minheap 中删除(但计时器对象的删除可能会或可能不会留给创建它的任务),并且堆的其余部分是 minheap-ified;即,重新排列以满足 minheap 属性。
一个工作和单元测试的实现是here,可能包含错误(摘自我的应用程序),但我认为它可能对某人有所帮助。该实现是基于多进程(fork()
ed-process)的(并且pthread
在主任务(进程)中也使用s),并使用POSIX共享内存和POSIX消息队列进行进程之间的通信。
在 Windows 中,您有SetTimer
- http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx
示例代码:
#define STRICT 1
#include <windows.h>
#include <iostream.h>
VOID CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
cout << "CALLBACK " << dwTime << '\n';
cout.flush();
}
int main(int argc, char *argv[], char *envp[])
{
int Counter=0;
MSG Msg;
UINT TimerId = SetTimer(NULL, 0, 2000, &TimerProc); //2000 milliseconds
cout << "TimerId: " << TimerId << '\n';
if (!TimerId)
return 16;
while (GetMessage(&Msg, NULL, 0, 0))
{
++Counter;
if (Msg.message == WM_TIMER)
cout << "Counter: " << Counter << "; timer message\n";
else
cout << "Counter: " << Counter << "; message: " << Msg.message << '\n';
DispatchMessage(&Msg);
}
KillTimer(NULL, TimerId);
return 0;
}