Подтвердить что ты не робот

С++ Реализация временной функции обратного вызова

Я хочу реализовать некоторую систему в С++, чтобы я мог вызвать функцию и попросить вызвать другую функцию в X миллисекундах. Что-то вроде этого:

callfunctiontimed(25, funcName);

25 - количество миллисекунд перед вызовом функции.

Я хотел бы знать, требуется ли многопоточность для этого, а затем использовать некоторую функцию задержки? Помимо использования указателя функции, как бы работала такая функция?

4b9b3361

Ответ 1

Для портативного решения вы можете использовать boost:: asio. Ниже приведена демоверсия, которую я написал некоторое время назад. Вы можете изменить

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

Ответ 2

Вы хотите, чтобы он был асинхронным, чтобы обратный вызов выполнялся, когда 25 миллисекунд закончились, не блокируя основной исполняемый поток? Если это так, вы можете выполнить обратный вызов в отдельном потоке из выполняемой вами функции обратного вызова таймера/времени.

Если вы не используете многопоточность, то ваша основная или вызывающая функция callfunctiontimed (25, funcName); будет блокироваться, пока вы запускаете sleep/usleep. Это твой выбор теперь о том, какое поведение вы хотите реализовать.

Реальное решение не будет таким простым, как многопоточное или нет. Есть такие вещи, как, как вы сохраняете информацию о таймерах/обратных вызовах, учитывая, что функция может быть вызвана несколько раз с различными тайм-аутами и функциями.

Один из способов сделать это будет следующим:

  • Создайте отсортированный список таймеров/обратных вызовов, отсортированный по времени истечения срока действия.
  • В основном потоке и одном потоке, который смотрит на обратные вызовы/таймеры, назовите его потоком таймера.
  • При добавлении нового обратного вызова добавьте его в отсортированный список.
  • Поток таймера может быть инициализирован для ожидания наименьшего количества времени в отсортированном списке или в голове. Повторно инициализируйте его, когда будут добавлены новые обратные вызовы. Там будет определенная математика и условия, чтобы позаботиться.
  • По мере того, как поток таймера выполняется спящим, он удаляет и просматривает головку списка и выполняет указатель функции в новом потоке. Поток таймера повторно инициализируется со временем сна в новом заголовке списка.

    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 делает только одно: она изменяет минимальный уровень событий обратного вызова, установленных в будущем. Минерал выбирает следующее событие для запуска в O(1) и может Minheapify оставшиеся в O(log n) для n событий таймера. Он также может вставить новое событие таймера в O(log n) (читайте мягкое введение в кучи здесь).

Когда срабатывает таймер, планировщик minheap проверяет, является ли это периодическим таймером, таймером одного снимка или таймером, который будет выполняться определенное количество раз. Соответственно, объект таймера либо удаляется из Minheap, либо снова вставляется обратно в minheap со следующим временем выполнения. Если объект таймера должен быть удален, он удаляется из minheap (но удаление объекта таймера может или не может быть оставлено заданию, которое его создало), а остальная часть кучи - minheap-ified; т.е. перегруппированы, чтобы удовлетворить свойству minheap.

Рабочая и внедренная версия реализована здесь и может содержать ошибки (выдержки из моего приложения), но я думал, что это может помочь кому-то. Реализация представляет собой многопроцессорный процесс (fork() ed-process) (а также использует pthread в основной задаче (процессе)) и использует POSIX разделяемую память и очереди сообщений POSIX для связи между процессами.

Ответ 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;
}