c++实现定时回调函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12904098/
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 16:44:48  来源:igfitidea点击:

c++ Implementing Timed Callback function

c++callbackfunction-pointerstiming

提问by DavidColson

I want to implement some system in c++ so that I can call a function and ask for another function to be called in X milliseconds. Something like this:

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

callfunctiontimed(25, funcName);

25 being the amount of milliseconds before the function should be called.

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

I would like to know if multithreading is required for this and then use some delay function? Other than using function pointer how would a feature like this work?

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

采纳答案by Sonny

Many folks have contributed good answers here on the matter, but I will address the question directly, because I had a similar problem a couple of years ago. I could not use Boost for several reasons--I know Boost has excellent use in a lot of open source software. Moreover, I really wanted to understandtimers and callbacks specifically as it pertains to Linux based environments. So, I wrote my own.

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

Fundamentally, I have a Timerclass and a TimerCallbackclass. A typical callback, implemented as a inherited class of the TimerCallbackclass, will place the operations to be executed upon callback in the triggered ()method, implemented specifically for the needs.

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

Per the usual semantics, a Timerobject is associated with a callback object, which presumably contains all the required information needed for the callback to execute. The timer scheduling is managed by one environment-wide timer minheap which has to be maintained in a separate thread/process. This minheap task does only one thing: it minheapifies the minheap of callback events set in the future. A minheap selects the next event to fire in O(1)and can minheapify the remaining in O(log n)for ntimer events. It can also insert a new timer event in O(log n)(Read a gentle introduction to heaps here).

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

When a timer fires, the minheap scheduler checks if it is a periodic timer, one shot timer or a timer that will execute a specific number of times. Accordingly, the timer object is either removed from the minheap or reinserted back into the minheap with the next execution time. If a timer object is to be removed, then it is removed from the minheap (but the timer object deletion may or may not be left to the task that created it) and the rest of the heap is minheap-ified; i.e., rearranged to satisfy the minheap property.

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

A working and unit tested implementation is here, and may contain bugs (excerpted from my application), but I thought it may help someone. The implementation is multi-process (fork()ed-process) based (and also uses pthreads in the main task (process)), and uses POSIX shared memory and POSIX message queues for communication between the processes.

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

回答by Gob00st

For a portable solution, you can use boost::asio. Below is a demo I wrote a while ago. You can change

对于便携式解决方案,您可以使用 boost::asio。下面是我前一段时间写的一个演示。你可以改变

t.expires_from_now(boost::posix_time::seconds(1));

to suit you need say make function call after 200 milliseonds.

为了适应你需要说在 200 毫秒后调用函数。

t.expires_from_now(boost::posix_time::milliseconds(200)); 

Below is a complete working example. It's calling repeatedly but I think it should be easy to call only once by just change a bit.

下面是一个完整的工作示例。它反复调用,但我认为只需稍作更改就可以轻松调用一次。

#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

回答by Gaurav Sinha

Do you want it to by asynchronous so that the callback gets executed when 25 miliseconds are over without blocking the main executing thread? If so, you can execute the callback in a separate thread from the timer / timed callback function you implement.

您是否希望它异步执行,以便在 25 毫秒结束时执行回调而不阻塞主执行线程?如果是这样,您可以在与您实现的计时器/定时回调函数不同的线程中执行回调。

If you do not use multithreading, then your main or the calling function of callfunctiontimed(25, funcName); would block while you run a sleep / usleep. It is your choice now as to what behavior you want to implement.

如果你不使用多线程,那么你的 main 或 callfunctiontimed(25, funcName); 的调用函数;运行 sleep / usleep 时会阻塞。您现在可以选择要实现的行为。

The real solution would not be as simple as to multithread or not. There are things like, how do you keep different timers/callbacks information considering the function can be called multiple times with different timeouts and functions.

真正的解决方案不会像多线程那样简单。有一些事情,例如,考虑到可以使用不同的超时和函数多次调用该函数,您如何保留不同的计时器/回调信息。

One way to do it, would be like this:

一种方法是这样的:

  1. Create a sorted list of timers/callbacks, sorted based on the time to expire.
  2. Have on main thread and one thread which looks at callbacks/timers, call it timer thread.
  3. When a new callback is added, add it in the sorted list.
  4. The timer thread could be initialized to wait for the least amount of time in the sorted list, or the head. Re-initialize it as new callbacks get added. There would be some math and conditions to take care of.
  5. As the timer thread is done sleeping, it removes and looks at head of the list and executes the function pointer in a new thread. Timer thread is re-initialized with sleep time on the new head of the list.

    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     
        }
    }
    
  1. 创建一个排序的计时器/回调列表,根据到期时间排序。
  2. 有一个主线程和一个查看回调/计时器的线程,称之为计时器线程。
  3. 添加新回调时,将其添加到排序列表中。
  4. 计时器线程可以初始化为等待排序列表或头部中的最少时间。随着新的回调被添加,重新初始化它。会有一些数学和条件需要处理。
  5. 当定时器线程完成睡眠时,它移除并查看列表的头部并在新线程中执行函数指针。定时器线程在列表的新头部重新初始化睡眠时间。

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

Hopefully this gives an idea of what I meant, although this is not perfect and has several problems.

希望这能让我明白我的意思,尽管这并不完美并且有几个问题。

回答by Software_Designer

In windows, you have SetTimer- http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx

在 Windows 中,您有SetTimer- http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx

Sample code:

示例代码:

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