- ベストアンサー
プログラムが3日目以降から調子が悪くなる
C言語を用いてある測定装置のデータを5秒おきに取得するプログラムを作成したのですが、3日11時間14分(61,128回目のループ)を過ぎると取得する時間が15秒後に一回になっていました。おそらくループが原因だと思うのですが自分で解決できなくて困っています。 ループの入り口は for(p=1;p<=loop;p++){ でpの型はlong型です。 5秒間に1つずつpの値は増えていくのですが、long型なので大きさは十分だと思います。 5秒間待つループは while((((clock()-start1)/ CLOCKS_PER_SEC ) % TIME )!= 0 ) { } // TIME=5 としてあり、start1には一番最初のループの前で取得した時間データが入っています。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
#3です。 もし、 for(p=1;p<=loop;p++){ ・・・・//ここの処理が必ず1秒超なら問題なし while(条件判定) { } で ・・・・の処理が1秒以内に完了することがあるなら、 ・・・の処理を1秒間に2回以上行う可能性があります。 これを排除したいなら(5秒間に1回だけ行いたいなら) 以下のようにすべきです。 time_t time0,time1; として time0 = time(NULL); for(){ ・・・・ while(1){ Sleep(1); time1 = time(NULL); if (time0 == time1) continue; if ((time1%TIME)!= 0) continue; time0 = time1; } } 考え方は、前回実行した時間(単位:秒)と同じなら、変わるまでスリープする。そして、5の倍数になるまで、スリープする。 になります。
その他の回答 (7)
- Tacosan
- ベストアンサー率23% (3656/15482)
えぇと.... インターバルタイマじゃダメなんでしょうか? 「一定時間間隔で何かしたい」というなら最初の選択肢のような気がするんですが.
お礼
回答ありがとうございます。 皆様のおかけで何とかできそうです。ありがとうございます。
- chie65536(@chie65535)
- ベストアンサー率44% (8741/19839)
>OSの環境はwindowsでvisual stdio 2005を使用しています。 どの回答の方法も「% TIME」の式を使っている限り、処理されずに取りこぼす可能性がある。 もし「仮想メモリのスワップ動作で、1秒間以上、ディスクアクセスが続く」と言う状況で、その「1秒間」が「ほげほげ % TIME の式が 0 になる1秒間」であったら、取りこぼしが起き、計測は10秒に1回になる。 基本的に「5秒に1回計測する処理で、5秒待つのが間違い」である。 じゃ「5秒待たずに、どうすんの」かと言うと「待たないのが正解」なのだ。 つまり「5秒待ったりせず、常に時計の監視を続け、前回の計測時刻から、5秒以上経ってたら、次の計測をする」って事。ここで重要なのが「常に時計の監視を続けている」という事と「5秒以上の判断」という部分。 特に重要なのが「5秒以上」である。「5秒きっかり」ではないので「6秒後でも、7秒後でも計測する」ので、誰かにCPUを専有されていても大丈夫なのだ。 以下の3つの「時間経過の図」を見てほしい。どれも「開始してからの20秒間」の図である。 記号の意味 ■:計測処理している時間 □:待ち合わせしている時間 _:OSや他のタスクにCPUが専有された時間 ・単純に「5秒待つ」場合(□が5個になるまで待つ) ⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒ ■■□□_□□□■■_□□□□□■_□□ 20秒に3回しか実行されず、明らかに正しくない。 ・「% TIME」を用いて5で割った余りが0になるまで待つ場合 ⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒ ■■□□_■■□□□_□□□□■□_□□ ちょっと見には正しそうだが、3回目の計測タイミングで「他のタスクに処理を奪われ、5で割った余りが0になる秒が、過ぎてしまった」ので、20秒間に3回しか計測されていない。 ・「前回の計測から5秒以上経過したか」で処理した場合 ⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒ ■■□□_■■□□□_■■□□■□_□□ 3回目の計測は「期待した瞬間」よりも1秒遅れて「前回の計測から6秒後に開始」されているが、取りこぼしされるよりはマシであるので、このパターンが「最適」と言える。 つまり、以下のようにプログラムすべき。 -------------------- #define TIME 5L //time_t型は「typedef long time_t」と仮定する //なので、単純に加算、比較が可能と仮定する time_t next; //最初だけ「秒が変わるまで待つ」こと。 //じゃないと「秒が変わる寸前に最初の時刻を拾ってしまう」と //最初が「4秒間隔」になってしまう。 time(&next); while (next == time(&NULL)) { Sleep(10); } //秒が変わったばかりの「フレッシュな現在時刻」を拾う time(&next); next += TIME; //ループ毎に「(p > loop)」の判定はしない for(p = 1;;) { //「次の計測タイミングになったか、過ぎた場合」と言う事が重要 if (time(NULL) >= next) { //「次の計測タイミング」を更新する next += TIME; //終了判定の「(p > loop)」は「pが変化した時のみ」で良い if (++p > loop) break; //計測を行う keisoku(); continue; } //要は「秒が変わったか?」が重要なので「0.2秒間隔」など //充分に長いインターバルで構わない Sleep(200); //0.2秒待つ。 } -------------------- 上記の処理では「次に計測すべき時刻を5秒づつ進めているだけ」で「5秒待つ」とか「5で割った余りが0になる」などの「愚かな判定方法」は使用していない。 従って、タイミングに多少の前後はあっても「必ず、5秒に1回は、計測ルーチンを呼ぶ」のが保証される。
お礼
回答有難うございます。 図を使った説明で大変分かりやすかったです。 データの取りこぼしは大変痛いのでこんな考え方があるのかと参考になりました。
- tatsu99
- ベストアンサー率52% (391/751)
#3です。 windowsなら、 while((time(NULL)%TIME)!=0){ Sleep(1); //1ミリ秒スリープ } としてみて下さい。 尚、time()はtime(NULL)の誤りでした。失礼しました。 http://msdn.microsoft.com/ja-jp/library/1f4c8f33(VS.80).aspx
- php504
- ベストアンサー率42% (926/2160)
ずっと動かしていたらいつかclock( )がオーバーフローします
お礼
回答ありがとうございます。 どうやらclock()が原因のようです。
- tatsu99
- ベストアンサー率52% (391/751)
OSが提示されていないので、linux 32ビット環境の前提で、コメントしします。clock()関数の戻り値は、システム起動後、約72分で、clocl_t型の最大値(unsigned longの最大値)に達します。最大に達した後は、0に戻り、そこから増え続けます。従って、約72分ごとに、同じ値が戻ることになります。 従って、((clock()-start1)が(正しい意味で)意味があるのは、システム起動後の72分までの間ということになります。 また、 ((clock()-start1)が負の値になることも予想されます。 従って、5秒間に1回を実現したいのであれば、 while((time()%TIME)!=0){ } とすべきです。time()の場合は、32ビットマシンでも2030年程度まではオーバーフローしませんので使用可能です。 但し、#2の方が述べているように、sleepを一切行っていないのは、非常に問題のある使い方です。 正確に5秒のインタバルを保証したいのか、それとも5秒スリープ後に計測すればかまわないのかの何れかにより、対応の方法も異なりますが、いずれにせよ、sleep,usleep,nanosleeepのいずれかを使用すべきです。 http://www.linux.or.jp/JM/html/LDP_man-pages/man2/time.2.html http://www.linux.or.jp/JM/html/LDP_man-pages/man3/usleep.3.html http://www.linux.or.jp/JM/html/LDP_man-ges/man2/nanosleep.2.html
お礼
回答ありがとうございます。 OSの環境はwindowsでvisual stdio 2005を使用しています。 どうやら、clock()関数がまずかったようですね。 計測は5秒間ごとにデータを取得することで、5秒スリープ後の計測ではないです。ですので、Sleep(5000)とするのは駄目でした。 >>while((time()%TIME)!=0){ この関数が使えそうですがtime()は引数が足りなくて駄目でしたので、もう一工夫してみようと思います。ありがとうございました。
- nda23
- ベストアンサー率54% (777/1415)
特殊な組込みといった環境なのでしょうか? 通常のアプリケーションの環境なら、「5秒待つ」はsleep関数を使う 方法を考えるべきです。「自力のループで待機」というのはCPUの タイムスライスからみると、とても「もったいない」使い方です。 あくまで、一般論です。OSが特殊な場合(sleep対応できない)も あります。 この場合はタイマー割り込みの回数を数える等の方法があります。
お礼
回答ありがとうございます。 >>特殊な組込みといった環境なのでしょうか? OSはwindowsでVISA関数という変わった関数を使っていますが、これが特殊な組み込みなのかは私には勉強不足で分からないです。 >>「自力のループで待機」というのはCPUのタイムスライスからみると、とても「もったいない」使い方です。 おっしゃるとおり、CPU使用率が常に80%~100%になっておりどうしたらいいか困っておりました。また、プログラムの調子が良いときは80%~100%を行ったりきたりしてたのですが、プログラムの調子が悪いときは0%~100%とCPU使用率が変化していたのでひょっとしたら「自力のループで待機」がよくなかったかもしれません。 一度sleep関数を使って様子を見てみようと思います。ありがとうございました。
- usatan2
- ベストアンサー率37% (163/436)
自信はありませんが、 >start1には一番最初のループの前で取得した時間データが入っています。 これがまずいのではないですか? ループの直前に取得すれば問題が回避されると思います。
お礼
回答ありがとうございます。 私の文章の書き方が悪くてすいません。ループの直前に取得してあります。
お礼
回答有難うございます。 time(NULL)で動作させてみたところちゃんと動きました!