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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C++11

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

Q&A

5回答

7626閲覧

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

yuba

総合スコア5568

C++11

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

4グッド

3クリップ

投稿2016/03/29 05:52

編集2016/03/29 05:54

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

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

raccy, KiyoshiMotoki, afroscript, tkturbo👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

こんにちは。

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

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

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

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

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

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

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

投稿2016/03/29 07:10

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tkturbo

2016/03/29 07:28

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

2016/03/29 07:47

確かに、タイムアウトイベント発生時の処理の重さ、というかタイムアウトイベントをどんなスレッドがどう受け取るのかによっていろいろ変わってくるのではないかというのはご指摘の通りです。 (逆に言うと、システム全体の駆動機構デザインに関わるパターンこそ今求めているものになるとも言えます) そのシステム全体のデザインはまだぼやっとしているのでまだ再考の余地はあるのですが、無通信発生時処理をやるスレッドは無限ループ型で基本的にはブロックしており無通信発生イベントを受けてブロックが解除されるような形態を考えています。 catsforepawさんのご提案の通りの、WaitForSingleObjectかcondition_variableによる制御です。
Chironian

2016/03/29 07:51 編集

最近はあまり制御ソフトを開発していないので、漏れがあるかも知れませんが、記述してみます。 典型的には、タイマリスタート処理との競合です。タイムアップ処理をするのかキャンセルするのか決めるために、何らかの変数を設けると思います。そして、大抵リード・モディファイ・ライト(他方が処理を始めてないか確認して始めてなければ自分が始める)になるのて、データ競合が発生します。 また処理決定後、処理完了前に処理しない方のイベントが発生すると思わぬ動作に至ることがありますので、その競合を避ける仕組みも必要になります。 「処理判定直前~処理しない方のイベントのキャンセル完了」までMutexを確保することが多いように思います。 そして、キャンセルされた方も既に処理を始めている可能性があるためMutexの獲得に成功した時、自分がキャンセルされていないか確認する必要があります。 キャンセル処理をマルチスレッドで行うのはハマりやすく、いつも苦労する部分です。 ああ、書いている間にyubaさんのコメントがありました。このコメントはtkturboさんへのコメントです。
tkturbo

2016/03/29 07:56

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

2016/03/29 08:02

yubaさん。 イベントが発生しないことを監視する専用スレッドを別に設けるのであれば、catsforepawさんが仰るように何からのタイムアウト付きのイベント待ちAPIを用いるのが良いように思います。 通信処理はそのイベントを発火させ、専用スレッドはイベント待ちAPIからタイムアウトで抜けてきた時だけ、タイムアウト処理すれば良いように思います。 ループは非常に小さいので、タイムアウト処理した時を除き、そのOSのCPU割り当て時間程度の応答性能を確保できる筈です。 タイムアウト処理した後の再開には要注意ですね。タイムアウト処理中に通信が発生した時、その通信の有無により、次のタイムアウト発生に影響するようでしたら、何か対策が必要になります。その対策はタイムアウト処理の内容次第と思います。
guest

0

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


追記

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

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

投稿2016/03/29 07:33

編集2016/03/29 08:08
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2016/03/29 07:41

イベント発火の伝達機構としてはおっしゃるとおりWindowsのWaitForSingleObjectか、C++汎用でいこうとすると、うーん、condition_variable+フラグ変数になりますかね。 ただ、それを間違いなく発火させる仕組みを今回考えているところになります。
catsforepaw

2016/03/29 08:07

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

2016/03/29 08:35

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

0

うーん。

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

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

投稿2016/03/29 06:12

tkturbo

総合スコア5572

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2016/03/29 06:16

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

2016/03/29 06:19

「イベントが発生しないというイベント」の検知ってのは結構厄介ですねぇ >停止&破棄の処理とタイマー発火がぶつからないようにetc 確かにこの辺も悩ましいですねぇ
tkturbo

2016/03/29 07:01

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

2016/03/29 07:05

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

2016/03/29 07:25

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

2016/03/29 07:37

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

0

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

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

投稿2016/03/30 08:37

編集2016/03/30 08:39
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/03/30 09:14

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

2016/03/30 09:22

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

0

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

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

投稿2016/03/29 06:05

keytan

総合スコア51

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2016/03/29 06:13

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

2016/03/29 06:30

「あるイベント」到着時点でタイマーを立てるということですね。そのタイマーが発火した時点でなんらかの判断をして「特定のイベント」を発火させるやり方。 ごめんなさい、ちょっと読み違えていました。 で、その「なんらかの判断」のところで「フラグ」を参照するわけですがそのフラグはどのようなデータ構造になりますでしょうか。例えば10秒間の無通信時間を検出するとして、 00:00:00 00:00:03 00:00:06 00:00:16 に通信があった場合、フラグはどのように変化することを意図なさっていますか。00:00:16の通信については10秒間タイムアウトに「ギリギリ間に合った場合」「ギリギリ間に合わなかった場合」にそれぞれどうなるかなど。 この辺を詳細に考えるとどんどん複雑になってしまって頭を抱えているところなのです。
keytan

2016/03/29 06:34

ちなみにタイマーでイベントが発生する機能は CPUに付いている機能でハードウェアで 実装されているので正確です。 ちなみに私のブログ プログラムを停止してタイマーで プログラムを一定間隔で起動しています。 どのCPUでもタイマーイベントはできますよ http://plaza.rakuten.co.jp/keytanosaka/diary/201103150000/
keytan

2016/03/29 06:42

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

2016/03/29 06:53

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

2016/03/30 07:46

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問