web-dev-qa-db-ja.com

c ++時限コールバック関数の実装

あるシステムをc ++で実装して、関数を呼び出し、別の関数をXミリ秒で呼び出せるようにしたいと考えています。このようなもの:

callfunctiontimed(25, funcName);

25は、関数が呼び出されるまでのミリ秒数です。

これにマルチスレッドが必要かどうかを知り、遅延関数を使用しますか?関数ポインターを使用する以外に、このような機能はどのように機能しますか?

15
DavidColson

多くの人々がこの問題について良い答えを出しましたが、私は数年前に同様の問題を抱えていたので、直接質問に答えます。いくつかの理由でBoostを使用できませんでした-Boostは多くのオープンソースソフトウェアで優れた用途があることを知っています。さらに、特にLinuxベースの環境に関係するため、タイマーとコールバックを理解することを本当に望んでいました。それで、私は自分で書きました。

基本的に、TimerクラスとTimerCallbackクラスがあります。 TimerCallbackクラスの継承クラスとして実装される典型的なコールバックは、コールバック時に実行される操作を、特にニーズに合わせて実装されているtriggered ()メソッドに配置します。

通常のセマンティクスによれば、Timerオブジェクトはコールバックオブジェクトに関連付けられており、おそらくコールバックの実行に必要なすべての必要な情報が含まれています。タイマースケジューリングは、環境全体の1つのタイマーminheapによって管理され、別のスレッド/プロセスで維持する必要があります。このminheapタスクは1つのことだけを実行します。それは、将来設定されるコールバックイベントのminheapをminheap化します。 minheapはO(1)で起動する次のイベントを選択し、nタイマーイベントの残りをO(log n)で最小化できます。また、O(log n)に新しいタイマーイベントを挿入することもできます(ヒープの穏やかな紹介 here )。

タイマーが起動すると、minheapスケジューラーは、それが定期的なタイマー、ワンショットタイマー、または特定の回数実行されるタイマーであるかどうかをチェックします。したがって、タイマーオブジェクトは、minheapから削除されるか、次の実行時間でminheapに再挿入されます。タイマーオブジェクトが削除される場合、タイマーオブジェクトはminheapから削除されます(ただし、タイマーオブジェクトの削除は、それを作成したタスクに委ねられる場合と残されない場合があります)。残りのヒープはminheap化されます。つまり、minheapプロパティを満たすように再配置されます。

動作していてユニットテスト済みの実装は here であり、バグ(アプリケーションからの抜粋)が含まれている可能性がありますが、誰かの助けになると思いました。実装はマルチプロセス(fork() ed-process)ベースであり(メインタスク(プロセス)でもpthreadsを使用)、POSIX共有メモリとPOSIXメッセージキューを使用して、プロセス。

6
Sonny

ポータブルソリューションの場合は、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
17
Gob00st

メインの実行スレッドをブロックせずに25ミリ秒が経過したときにコールバックが実行されるように、非同期で実行しますか?その場合は、実装するタイマー/時限コールバック関数とは別のスレッドでコールバックを実行できます。

マルチスレッドを使用しない場合、メインまたはcallfunctiontimed(25、funcName);の呼び出し関数。あなたがsleep/usleepを実行している間ブロックします。実装したい振る舞いについては、あなたの選択です。

実際の解決策は、マルチスレッドの場合ほど単純ではありません。たとえば、関数が異なるタイムアウトと関数で複数回呼び出される可能性があることを考慮して、異なるタイマー/コールバック情報をどのように保持するかなどです。

それを行う1つの方法は、次のようになります。

  1. 有効期限に基づいてソートされた、タイマー/コールバックのソートされたリストを作成します。
  2. メインスレッドと、コールバック/タイマーを確認する1つのスレッドをタイマースレッドと呼びます。
  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     
        }
    }
    

これは完璧ではなく、いくつかの問題がありますが、うまくいけば、私が何を意味するのかがわかります。

6
Gaurav Sinha

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