質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.48%

  • C++11

    108questions

    C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

「○○秒間途切れたら」という条件のスマートな書き方

受付中

回答 5

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 2,105

yuba

score 5171

やりたいことは一定の無通信時間に対する処理なのですが、もう少し一般的な形で表現して
●あるイベントが一定時間発生しなかったときに特定のイベントを発火させたい
というとき、どのような書き方がスマートですか?

ごく簡単には、「あるイベント」を受け取るたびに「最終時刻」を記録しておいて1秒ごとのタイマで最終時刻と現在時刻を比較すればいいですし、まあこれでソフトウェア要件は十分満たすことが大半だと思いますが、もう少し厳密に「最後のイベント発生からきっかり○○秒後」にイベントを発火させる書き方、イディオムのようなものがあればご教示いただけませんでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+5

こんにちは。

ウォッチドッグ・タイマなどで良く使われますが、実装に用いるハードウェアやOSのリソースにより、その実装方法や注意点は様々ですので、あまり定型的なパターンはないように思います。

ポイントは①タイムアップした時の処理を実行する仕組みと②タイムアップとタイマリスタートがほぼ同時に発生した時にどのようにして制御するのかの2点と思います。
①が決まらないと②を検討できないのでご提示の条件だけでは、処理方針を決めることはできないです。

①を行うために定期的にポーリングする方法は、データ競合の心配がなくプログラムが単純ですので、応答が遅くて良い場合に用いています。

WindowsのSetTimer()APIのようにコールバックが同じスレッドで呼ばれるような場合は、やはりデータ競合の心配はないので比較的楽にかけますが、他の処理にかまけていると応答が遅れ、タイムアップとタイマリスタートのイベントが「同時」に処理待ちになる問題を回避できません。要件によってはどちらが先か記録する仕組みが必要になるかも知れません。

①としてコールバック関数が別スレッドで呼ばれる場合はデータ競合の心配が出てくるので注意深い設計が必要ですが、応答性能を確保できます。

どの方法も一長一短で、要件に応じて適切な方法を選択するしかないと思います。

ちなみにウォッチドッグ・タイマの場合は、一般にタイムアップでシステム・リセットで、①をハードウェアにて実装する場合が多いです。(そもそもソフトウェアの暴走対策なのでソフトウェアで実装すると効果をほとんど期待できない。)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/29 16:28

    失礼ですが、「①としてコールバック関数が別スレッドで呼ばれる場合」のデータ競合回避の事例などご存知でしたらこちらも教えていただけると、私が勝手に喜びます。

    キャンセル

  • 2016/03/29 16:47

    確かに、タイムアウトイベント発生時の処理の重さ、というかタイムアウトイベントをどんなスレッドがどう受け取るのかによっていろいろ変わってくるのではないかというのはご指摘の通りです。
    (逆に言うと、システム全体の駆動機構デザインに関わるパターンこそ今求めているものになるとも言えます)

    そのシステム全体のデザインはまだぼやっとしているのでまだ再考の余地はあるのですが、無通信発生時処理をやるスレッドは無限ループ型で基本的にはブロックしており無通信発生イベントを受けてブロックが解除されるような形態を考えています。

    catsforepawさんのご提案の通りの、WaitForSingleObjectかcondition_variableによる制御です。

    キャンセル

  • 2016/03/29 16:48 編集

    最近はあまり制御ソフトを開発していないので、漏れがあるかも知れませんが、記述してみます。

    典型的には、タイマリスタート処理との競合です。タイムアップ処理をするのかキャンセルするのか決めるために、何らかの変数を設けると思います。そして、大抵リード・モディファイ・ライト(他方が処理を始めてないか確認して始めてなければ自分が始める)になるのて、データ競合が発生します。
    また処理決定後、処理完了前に処理しない方のイベントが発生すると思わぬ動作に至ることがありますので、その競合を避ける仕組みも必要になります。

    「処理判定直前~処理しない方のイベントのキャンセル完了」までMutexを確保することが多いように思います。
    そして、キャンセルされた方も既に処理を始めている可能性があるためMutexの獲得に成功した時、自分がキャンセルされていないか確認する必要があります。
    キャンセル処理をマルチスレッドで行うのはハマりやすく、いつも苦労する部分です。

    ああ、書いている間にyubaさんのコメントがありました。このコメントはtkturboさんへのコメントです。

    キャンセル

  • 2016/03/29 16:56

    javaのmultiThreadingだとsynchronizedとかで制御できるのでMutex確保については意識していませんでした。参考になりました。あざっす!m(_ _)m

    キャンセル

  • 2016/03/29 17:02

    yubaさん。

    イベントが発生しないことを監視する専用スレッドを別に設けるのであれば、catsforepawさんが仰るように何からのタイムアウト付きのイベント待ちAPIを用いるのが良いように思います。
    通信処理はそのイベントを発火させ、専用スレッドはイベント待ちAPIからタイムアウトで抜けてきた時だけ、タイムアウト処理すれば良いように思います。

    ループは非常に小さいので、タイムアウト処理した時を除き、そのOSのCPU割り当て時間程度の応答性能を確保できる筈です。

    タイムアウト処理した後の再開には要注意ですね。タイムアウト処理中に通信が発生した時、その通信の有無により、次のタイムアウト発生に影響するようでしたら、何か対策が必要になります。その対策はタイムアウト処理の内容次第と思います。

    キャンセル

+2

うーん。

1.イベント発生時にタイマーオブジェクトを生成、内部変数に登録(登録済みのものは停止して破棄)
2.タイマーオブジェクトでイベント発生から一定時間経過後イベント発火
3.タイマーオブジェクトに対してObserverを準備しておき、Observerがイベント発火を監視

かなぁ。
Observerパターンから逃れらぬ。。。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/29 15:16

    これしかないですかね…

    > (登録済みのものは停止して破棄)

    ここの処理がとてもデリケートになる(停止&破棄の処理とタイマー発火がぶつからないようにetc)ので、この辺の泥沼に足を取られないようにと方法を模索しているところではあります。

    キャンセル

  • 2016/03/29 15:19

    「イベントが発生しないというイベント」の検知ってのは結構厄介ですねぇ

    >停止&破棄の処理とタイマー発火がぶつからないようにetc

    確かにこの辺も悩ましいですねぇ

    キャンセル

  • 2016/03/29 16:01

    ふと思ったのですが、WindowsやandroidのディスプレイOFFってどういう実装なんですかね?
    「一定時間イベントが発生しないというイベント」を検知する好例のような気がしてきました。

    キャンセル

  • 2016/03/29 16:05

    ObserverといえばRx、
    RxでいうところのThrottleに相当するのでRx for Cppの実装はどうなっているのかなー
    と思ったらTBDでした

    キャンセル

  • 2016/03/29 16:25

    > ozwkさん
    Rxは初耳だったので今度ソース読んでみます。(とりあえずreadmeは落としてきた。)

    キャンセル

  • 2016/03/29 16:37

    ozwkさん
    > RxでいうところのThrottleに相当

    リアクティブプログラミングはノーマークだったので、まさにこれに該当するトピックが存在したの驚きです。
    ありがとうございます。

    キャンセル

+2

要は、タイムアウト付きのイベント待ちですよね。そのような処理にはWindows APIのWaitForSingleObjectが真っ先に思い浮かびます。その仕組みだと「あるイベント」と「タイムアウト時間」のタイミングが重なっても処理されるのはどちらか一方ですし、タイムアウトはイベントとして処理するわけではないのでリセットとかキャンセルとかも不要です。
Windows以外では自力でそれに相当するイベント待ちの処理を実装しないといけませんが。


追記

メッセージ駆動方式はどうでしょうか。
イベントをキューに入れてシリアライズし、「あるイベント」メッセージ処理でタイマーを開始し、同時にキューにタイマーメッセージが残っていたらキャンセル(削除)する。

Windowsでは、メッセージ処理専用のウィンドウを作って、イベントをシリアライズして処理するということをよくやります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/29 16:41

    イベント発火の伝達機構としてはおっしゃるとおりWindowsのWaitForSingleObjectか、C++汎用でいこうとすると、うーん、condition_variable+フラグ変数になりますかね。

    ただ、それを間違いなく発火させる仕組みを今回考えているところになります。

    キャンセル

  • 2016/03/29 17:07

    メッセージ駆動方式はどうでしょうか。
    回答の方に追記します。

    キャンセル

  • 2016/03/29 17:35

    シリアライズしてしまうのは確かに確実ですね。今回Win/MacなのでWindowsのウィンドウメッセージ機構でやってしまうわけにはいきませんが、競合の心配のないプログラミングに出来ます。

    キャンセル

+1

QtのQTimerなら簡単にrestart(startがそのままrestartを兼ねる実装)できるため、イベントが起きる度にrestartし続ければ簡単に実装できると思います。ただ、この方法ですとQt依存になりますし、GUIとか全体としてQtを使っていないと難しい(つまり、Qtのイベントループ上でしか動かない)です。ただ、簡単にマルチプラットフォームで実現するなら一番良いかなと思っています。

Qt依存を無くしたいので、C++のSTLのみだけで実現しようとは考えているのですが、なかなか方法が思い浮かばないです。誰かマルチプラットフォームな方法を私にも教えて欲しいです…(別スレッドでstd::this_thread::sleep_forで眠らせる…ってやったら、stopやrestartできないですよね…)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/30 18:14

    使ったことはないのですが、別スレッドでタイムアウト付きのイベント待ちstd::condition_variable::wait_for()を使えると思います。
    イベント成立条件をラムダ式で記述できるので、びっくりするほど強力そうです。

    キャンセル

  • 2016/03/30 18:22

    `future`でタイマーを作れそうな気がします。`wait_for`でタイムアウトしたらタイマーイベントを発動して、停止させたい場合はfutureオブジェクトに何か書き込むという具合に。書き込んだ値によって処理を分岐(stop/restart)させても良いかもしれません。
    メッセージキューなども作れそうな気がします。
    ただ、これまでC++標準のみでそういうものを作るという必要性がなかったので試したことはないです。

    キャンセル

-2

タイマーイベントで検索するとヒットします。

「あるイベント」が発生したらフラグを立てる
タイマーイベントでそれをチェックする
フラグが立っていなければ特定のイベントを発火させる
フラグをクリアする
それを繰り返す

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/29 15:13

    あ、その方法は質問文内で簡単な方法として上げている方法でして、それでも良いのですが精密ではない(最大でタイマー間隔の分だけ遅延がある)ので、精密な方法はないものか、というのが質問の主旨になります。

    キャンセル

  • 2016/03/29 15:21

    http://www.atmarkit.co.jp/fdotnet/dotnettips/372formstimer/formstimer.html
    タイマーイベントとはイベントハンドラーを使って
    指定した時間が来るとそのイベントに飛ぶものです。
    時差はありませんけど

    キャンセル

  • 2016/03/29 15:30

    「あるイベント」到着時点でタイマーを立てるということですね。そのタイマーが発火した時点でなんらかの判断をして「特定のイベント」を発火させるやり方。
    ごめんなさい、ちょっと読み違えていました。

    で、その「なんらかの判断」のところで「フラグ」を参照するわけですがそのフラグはどのようなデータ構造になりますでしょうか。例えば10秒間の無通信時間を検出するとして、
    00:00:00
    00:00:03
    00:00:06
    00:00:16
    に通信があった場合、フラグはどのように変化することを意図なさっていますか。00:00:16の通信については10秒間タイムアウトに「ギリギリ間に合った場合」「ギリギリ間に合わなかった場合」にそれぞれどうなるかなど。

    この辺を詳細に考えるとどんどん複雑になってしまって頭を抱えているところなのです。

    キャンセル

  • 2016/03/29 15:34

    ちなみにタイマーでイベントが発生する機能は
    CPUに付いている機能でハードウェアで
    実装されているので正確です。

    ちなみに私のブログ
    プログラムを停止してタイマーで
    プログラムを一定間隔で起動しています。
    どのCPUでもタイマーイベントはできますよ
    http://plaza.rakuten.co.jp/keytanosaka/diary/201103150000/

    キャンセル

  • 2016/03/29 15:42

    タイマーイベントは10秒ごとと一度設定したら
    10秒ごとに正確にイベントに飛びます。
    他の処理をしていても割り込み処理として
    実行されます。

    キャンセル

  • 2016/03/29 15:53

    うーん、やはり質問の主旨をあまり理解していただけていないご様子。
    タイマー機能そのものの正確さが問題なのではなく、タイマーを始動するタイミングとキャンセルするタイミング、そしてそれらの処理中にタイマー割り込みが飛び込んできた場合の扱い、といったあたりに難しさがあるのです。

    キャンセル

  • 2016/03/30 16:46

    PCはリアルタイムOSではありません。
    タイマーを使わないと正確性は得られません。
    タイマーの始動やキャンセルのタイミングを
    コントロールするのには矛盾を感じます。
    そもそもの考え方を変えた方がいいように思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C++11

    108questions

    C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。