メニュー
ブログ更新履歴
コンテンツ更新履歴
リンク
  • Magome
  • クラウドベースのMIDIシーケンサ
    音楽制作に興味のある方を対象に、スタンドアロンでも使え、ネットならではの面白さも兼ね備えた音楽制作アプリの提供を目指しています。
twitter

コード最適化とは違うネタです。すみません。さて。

"n秒間リトライを繰り返してみる。n秒過ぎたら諦めて抜ける"
というチュエーションは良くあると思います。

これを実現する為に書いたコードはリスト1です。
(わかりやすい例にするために、60秒間繰り返すというシンプルなコードにしてみました。)

  • List1
    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
    DWORD nBegin = ::GetTickCount();			// 処理開始のシステムカウンタを保持
    while(true){
    
    	Retry();								// ←適当な処理
    
    	DWORD n = ::GetTickCount() - nBegin;	// 処理開始から現在までの経過時間を求める
    	if( n >= 60000 ) break;					// 60秒以上経過してたら抜ける
    }

大事なのは、
DWORD n = ::GetTickCount() - nBegin; // 処理開始から現在までの経過時間を求める
の部分です。
現在時間 - 開始時間 という引き算をすることで、純粋な経過時間が取得出来ます。
こうすることで、もし仮の while の最中に、システムカウンタのオーバーフロウ(カウンタが0に戻る)しても、
正しい経過時間で判断することが出来ます。

まとめ。

説明しやすかったので、WindowsのAPIを使った例になりましたが、Windows以外でも理屈は同じです。特に組み込みソフトなどでは同じようなシチュエーションがあるのではないでしょうか。
この手の問題で一番厄介なのは、一見正しく動いてしまうことです。
しかも無限ループに陥る確立が極端に低い為、原因究明は困難です。
日ごろから色々なケースを想定したコーディングを心がける必要がありそうです。


トップ   凍結 差分 バックアップ 複製 名前変更 リロード印刷に適した表示   ページ新規作成 全ページ一覧 単語検索 最新ページの一覧   ヘルプ   最新ページのRSS 1.0 最新ページのRSS 2.0 最新ページのRSS Atom Powered by xpWiki
Counter: 345, today: 1, yesterday: 0
初版日時: 2005-12-25 (日) 08:43:00
最終更新: 2005-12-25 (日) 08:43:00 (JST) (989d) by takatsuka