現: 2016-03-31 (木) 17:00:32 takatsuka[3] [4] | |||
---|---|---|---|
Line 1: | Line 1: | ||
+ | コード最適化とは違うネタです。すみません。さて。 | ||
+ | "n秒間リトライを繰り返してみる。n秒過ぎたら諦めて抜ける" | ||
+ | というチュエーションは良くあると思います。 | ||
+ | |||
+ | これを実現する為に書いたコードはリスト1です。 | ||
+ | (わかりやすい例にするために、60秒間繰り返すというシンプルなコードにしてみました。) | ||
+ | |||
+ | -List1 | ||
+ | #prettify{{ | ||
+ | DWORD nEnd = ::GetTickCount() + 60000; // 処理開始から60秒後のシステムカウンタを求める | ||
+ | while(true){ | ||
+ | |||
+ | Retry(); // ←適当な処理 | ||
+ | |||
+ | DWORD n = ::GetTickCount(); // 現在のシステムカウンタを取得して、 | ||
+ | if( n >= nEnd ) break; // 60秒以上経過してたら抜ける | ||
+ | } | ||
+ | }} | ||
+ | |||
+ | このリスト1にはミスが潜んでいることは気づくでしょうか? | ||
+ | このコードは、一見正しく動きますが、ものスゴイ低い確率で無限ループに陥るケースが隠されています。 | ||
+ | |||
+ | WindowsAPI の GetTickCount は、システム起動からのカウンタ(経過時間)をms単位で取得します。 | ||
+ | GetTickCount から取得される経過時間は、DWORD(符号なし32Bit)ですので、値は 0~0xFFFFFFFF までです。 | ||
+ | ということは、Windowsの起動から 0xFFFFFFFF(約50日後) 経過後には、オーバーフロウしてカウンタは 0 に戻ります。 | ||
+ | |||
+ | リスト1に話を戻します。 | ||
+ | もし仮に、最初の ::GetTickCount() の戻り値が 0xFFFF0000 だった場合、 | ||
+ | 変数 nEnd には、0xFFFFEA60 (= 0xFFFF0000 + 60000 ) という値が入ります。 | ||
+ | |||
+ | すると、どういうことかというと、 | ||
+ | whileを抜けれる条件は、::GetTickCount()の戻り値が、 | ||
+ | 0xFFFFEA60 ~ 0xFFFFFFFF の間、つまり約5秒間しかありません。 | ||
+ | その5秒を逃してシステムカウンタが0に戻ってしまった場合は、whileを抜けることが出来ません。 | ||
+ | (厳密には50日後に抜けれる機会がまたやってきます) | ||
+ | |||
+ | |||
+ | というわけで、このようなミスに陥らない為には、リスト2のようにします。 | ||
+ | |||
+ | -List2 | ||
+ | #prettify{{ | ||
+ | DWORD nBegin = ::GetTickCount(); // 処理開始のシステムカウンタを保持 | ||
+ | while(true){ | ||
+ | |||
+ | Retry(); // ←適当な処理 | ||
+ | |||
+ | DWORD n = ::GetTickCount() - nBegin; // 処理開始から現在までの経過時間を求める | ||
+ | if( n >= 60000 ) break; // 60秒以上経過してたら抜ける | ||
+ | } | ||
+ | }} | ||
+ | |||
+ | 大事なのは、 | ||
+ | DWORD n = ::GetTickCount() - nBegin; // 処理開始から現在までの経過時間を求める | ||
+ | の部分です。 | ||
+ | 現在時間 - 開始時間 という引き算をすることで、純粋な経過時間が取得出来ます。 | ||
+ | こうすることで、もし仮の while の最中に、システムカウンタのオーバーフロウ(カウンタが0に戻る)しても、 | ||
+ | 正しい経過時間で判断することが出来ます。 | ||
+ | |||
+ | まとめ。 | ||
+ | |||
+ | 説明しやすかったので、WindowsのAPIを使った例になりましたが、Windows以外でも理屈は同じです。特に組み込みソフトなどでは同じようなシチュエーションがあるのではないでしょうか。 | ||
+ | この手の問題で一番厄介なのは、一見正しく動いてしまうことです。 | ||
+ | しかも無限ループに陥る確立が極端に低い為、原因究明は困難です。 | ||
+ | 日ごろから色々なケースを想定したコーディングを心がける必要がありそうです。 |
(This host) = https://thinkridge.com