c++实现定时回调函数

Posted

技术标签:

【中文标题】c++实现定时回调函数【英文标题】:c++ Implementing Timed Callback function 【发布时间】:2012-10-15 21:12:49 【问题描述】:

我想在 C++ 中实现一些系统,以便我可以调用一个函数并要求在 X 毫秒内调用另一个函数。像这样的:

callfunctiontimed(25, funcName);

25 是调用函数之前的毫秒数。

我想知道这是否需要多线程,然后使用一些延迟功能?除了使用函数指针之外,这样的功能如何工作?

【问题讨论】:

你应该研究std::function。它允许比函数指针更简洁的接口以及绑定变量等附加功能。 这不是一个简单的问题,也不是一个容易解决的问题。基本上,您必须围绕异步事件的多路复用处理来设计整个应用程序才能做到这一点。也就是说,一旦您了解它,这通常很容易。 您的目标是什么操作系统? 【参考方案1】:

对于便携式解决方案,您可以使用 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

【讨论】:

我不会对此投反对票,但是在您的答案中为两个不同的命名空间添加“使用”会自动使您的代码更难破译。考虑取出 using 并将命名空间放在一条线上。它使您更容易阅读您所引用的内容,并且无论如何您都会在生产代码中执行此操作(好的生产代码,而不是粗制滥造的周末战士编码生产代码)【参考方案2】:

您是否希望它异步执行,以便在 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     
        
    
    

希望这能说明我的意思,尽管这并不完美并且存在一些问题。

【讨论】:

这就是我想要的,程序的其余部分继续执行,然后在经过的时间后调用指定的函数 但是在多线程意义上,在“中断”意义上还是在事件循环意义上?超时应该拦截当前的执行主线还是等到达到适当的状态? 我不希望主程序被打断。我对多线程不太了解,只知道它可以做我想做的事【参考方案3】:

很多人在这里就此事提供了很好的答案,但我将直接解决这个问题,因为几年前我遇到了类似的问题。我不能使用 Boost 有几个原因——我知道 Boost 在很多开源软件中都有很好的用途。此外,我真的很想了解计时器和回调,因为它与基于 Linux 的环境有关。所以,我自己写了。

基本上,我有一个Timer 类和一个TimerCallback 类。一个典型的回调,实现为TimerCallback类的继承类,将回调时要执行的操作放在triggered ()方法中,专门针对需求实现。

根据通常的语义,Timer 对象与回调对象相关联,该对象可能包含执行回调所需的所有信息。计时器调度由一个环境范围的计时器 minheap 管理,该计时器必须在单独的线程/进程中维护。这个 minheap 任务只做一件事:它对将来设置的回调事件的 minheap 进行 minheap。 minheap 在O(1) 中选择下一个要触发的事件,并且可以将O(log n) 中的剩余事件minheapify 用于n 计时器事件。它还可以在O(log n) 中插入一个新的计时器事件(阅读对堆的温和介绍here)。

当定时器触发时,minheap 调度程序检查它是周期性定时器、单次定时器还是将执行特定次数的定时器。因此,计时器对象要么从 minheap 中删除,要么在下一个执行时间重新插入到 minheap 中。如果要删除计时器对象,则将其从 minheap 中删除(但计时器对象的删除可能会或可能不会留给创建它的任务),并且堆的其余部分是 minheap-ified;即,重新排列以满足 minheap 属性。

一个工作和单元测试的实现是here,可能包含错误(摘自我的应用程序),但我认为它可能对某人有所帮助。该实现基于多进程(fork()ed-process)(并且还在主任务(进程)中使用pthreads),并使用 POSIX 共享内存和 POSIX 消息队列进行进程之间的通信。

【讨论】:

chandank:代码的链接在上面的答案中。试试github.com/SonnyRajagopalan/TimerAndCallback。【参考方案4】:

在 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;

【讨论】:

需要一些修改: 1. using namespace std; 2. Cast &TimerProc with (TIMERPROC), 3. 设置不使用预编译头文件。除此之外,它工作正常。

以上是关于c++实现定时回调函数的主要内容,如果未能解决你的问题,请参考以下文章

思考5 定时器和callback回调函数

如何实现类的成员函数作为回调函数

c++回调函数详解及实现(lambda)

c++ 回调类成员函数实现

前端(十三)—— JavaScript高级:回调函数闭包循环绑定面向对象定时器

C++ 选择题总结(回调函数 || 类方法(实例方法)|| )