web-dev-qa-db-ja.com

c ++で定期的な時間間隔で関数を呼び出す必要があります

私はc ++でプログラムを書いています。そこでは、関数を定期的な時間間隔、たとえば10msごとに呼び出す必要があります。私はc ++で時間や時計に関連することをしたことがありません。これはすばやく簡単な問題ですか、それとも適切な解決策がないものですか?

ありがとう!

18
Execut1ve

単純なタイマーは次のように実装できます。

#include <iostream>
#include <chrono>
#include <thread>
#include <functional>

void timer_start(std::function<void(void)> func, unsigned int interval)
{
    std::thread([func, interval]() {
        while (true)
        {
            func();
            std::this_thread::sleep_for(std::chrono::milliseconds(interval));
        }
    }).detach();
}


void do_something()
{
    std::cout << "I am doing something" << std::endl;
}

int main() {
    timer_start(do_something, 1000);

    while(true);
}

この単純なソリューションは、タイマーを停止する方法を提供していません。タイマーは、プログラムが終了するまで動作し続けます。

17
user534498

質問を完了するために、@ user534498からのコードは、定期的な目盛り間隔を持つように簡単に調整できます。タイマースレッドループの開始時に次の開始時点を決定する必要があるだけで、sleep_until関数を実行した後のその時点。

#include <iostream>
#include <chrono>
#include <thread>
#include <functional>

void timer_start(std::function<void(void)> func, unsigned int interval)
{
  std::thread([func, interval]()
  { 
    while (true)
    { 
      auto x = std::chrono::steady_clock::now() + std::chrono::milliseconds(interval);
      func();
      std::this_thread::sleep_until(x);
    }
  }).detach();
}

void do_something()
{
  std::cout << "I am doing something" << std::endl;
}

int main()
{
  timer_start(do_something, 1000);
  while (true)
    ;
}
15
florgeng

申し訳ありませんが、それよりシンプルなデザインは見つかりませんでした。

スレッドとそれ自体に対するweak_ptrの両方を所有するクラスを、呼び出し可能オブジェクトが安全に参照できる「ホルダー」にすることができます。これは、オブジェクトが破壊された場合でも呼び出し可能オブジェクトが存在するためです。ぶら下がりポインタは必要ありません。

template<typename T>
struct IntervalRepeater {
    using CallableCopyable = T;
private:
    weak_ptr<IntervalRepeater<CallableCopyable>> holder;
    std::thread theThread;

    IntervalRepeater(unsigned int interval,
            CallableCopyable callable): callable(callable), interval(interval) {}

    void thread() {
        weak_ptr<IntervalRepeater<CallableCopyable>> holder = this->holder;
        theThread = std::thread([holder](){
            // Try to strongify the pointer, to make it survive this loop iteration,
            //    and ensure that this pointer is valid, if not valid, end the loop.
            while (shared_ptr<IntervalRepeater<CallableCopyable>> ptr = holder.lock()) {
                auto x = chrono::steady_clock::now() + chrono::milliseconds(ptr->interval);
                ptr->callable();
                this_thread::sleep_until(x);
            }
        });
    }

public:
    const CallableCopyable callable;
    const unsigned int interval;

    static shared_ptr<IntervalRepeater<T>> createIntervalRepeater(unsigned int interval,
            CallableCopyable callable) {
        std::shared_ptr<IntervalRepeater<CallableCopyable>> ret =
                shared_ptr<IntervalRepeater<CallableCopyable>>(
                        new IntervalRepeater<CallableCopyable>(interval, callable));
        ret->holder = ret;
        ret->thread();

        return ret;
    }

    ~IntervalRepeater() {
        // Detach the thread before it is released.
        theThread.detach();
    }

};

void beginItWaitThenDestruct() {
    auto repeater = IntervalRepeater<function<void()>>::createIntervalRepeater(
            1000, [](){ cout << "A second\n"; });
    std::this_thread::sleep_for(std::chrono::milliseconds(3700));
}

int main() {
    beginItWaitThenDestruct();
    // Wait for another 2.5 seconds, to test whether there is still an effect of the object
    //   or no.
    std::this_thread::sleep_for(std::chrono::milliseconds(2500));
    return 0;
}
0
user9335240

あなたはスレッディングを調べることができます:

これは、pthread.hを使用してCで実装された時間間隔制御関数です。これは、C++への単純なポートである必要があります。 特定の間隔で関数を実行する

0
JMercer

これはかなり古い投稿ですが、これに対する非常に簡単な解決策を見つけたので、共有したいと思います。

解決策は

#include "unistd.h"

上記のライブラリに含まれている関数を使用し、

usleep(int) 

たとえば、これは3000マイクロ秒ごとに関数を呼び出します

int main() {

    int sleep_time = 3000;
    while(true) { 

          usleep(sleep_time);

          call function;
    }

    return 0;
}

私はそれをシリアルコードとパラレルコードにも使用し、どちらの場合でも機能します!

Visual C++でコーディングしている場合は、定期的な関数を呼び出したいフォームにタイマー要素を追加できます(ここでは、フォームをMainForm、タイマーをMainTimerと呼びます)。 「イベント」にティックイベントへの呼び出しを追加します。デザイナーは.hファイルにそのような行を追加します:

this->MainTimer->Enabled = true;
this->MainTimer->Interval = 10;
this->MainTimer->Tick += gcnew System::EventHandler(this, &MainForm::MainTimer_Tick);

次に、各間隔(msで指定)で、アプリケーションはこの関数を呼び出します

private: System::Void MainTimer_Tick(System::Object^  sender, System::EventArgs^  e) {
   /// Enter your periodic code there
}
0
RawBean

それはあなたが間隔ごとに何をしているかに依存します-画面/フォームの特定の場所に時間/ティッカーまたは何か他のものを表示します。または、接続されているマシンに(ソケットまたはパイプを介して)通常のデータを送信する必要がある場合があります。本当に10ミリ秒の精度が必要ですか?

要件、特に精度要件に応じて、専用のスレッド'something'を実行してから、同じことを繰り返します。または、Windowsでは、各間隔でWM_TIMERイベントをトリガーするSetTimerを使用できます(スレッドは必要ありません)。待機可能タイマー、マルチメディアタイマーなどを使用することもできます。

最後に、しかし非常に重要です-プラットフォームとコンパイラの互換性が必要ですか?つまり、どのOSを使用するのでしょうか、それともプラットフォームに依存しない必要がありますか?探しているコンパイラ機能(C++ 11、C++ 14、またはC++ 11以前)。

0
Ajay