while文の危険性と対応(無限ループ防止)

謎のプリン語る。
プログラミングの役立つ情報とか、どうでもいい雑談とか書いてます。
一人書く人増えました。

while文の危険性と対応(無限ループ防止)

みやびプリン 500 316

500 320

while文の危険性と対応(無限ループ防止) - サムネイル

※この記事は6年以上前の記事です。
現在は状況が異なる可能性がありますのでご注意ください。

どうも。
仕事でかなり危ないミスをしてしまいました・・・。
関係各所、そして、お客様、本当に申し訳ございません。。。

さて、その原因となった、whileについて。

whileってのはとても便利な構造文で、
何回繰り返しすればわからない時に、条件に当てはまる間は、実行を繰り返すというもので、
おおよそほぼ全てのプログラム言語に存在するものだ。

例えば、運動会に来る家族の中で、パパがその日誕生日の家族を割り出し、その家族より前に入場してきた家族で、対象家族の子供と同じクラスの子供がいる家族を5組割り出すなんて時。
(ややこしくてすみませんが、whileをわざわざ使う状況を作りたかった)

<?php
// 運動会の日
$sportsDay = strtotime(date('Y-m-d'));
$sportsMonth = date('m', $sportsDay);
$sportsDay = date('d', $sportsDay)
// 入場した家族を格納する
$familys = array();
// 対象家族を入れる
$targetFamily = array();
// 割り出した家族を格納する
$matchingFamilyArr = array();

// 家族を入場させる
$familys[] = array('papaBirthday' => '1988-01-06', 'childClass' => 'A');
$familys[] = array('papaBirthday' => '1985-05-03', 'childClass' => 'B');
$familys[] = array('papaBirthday' => '1979-03-16', 'childClass' => 'B');
$familys[] = array('papaBirthday' => '1980-11-18', 'childClass' => 'C');
$familys[] = array('papaBirthday' => '1975-02-16', 'childClass' => 'A');
// .
// .
// .
// 対象がこの家族だとする
$familys[] = array('papaBirthday' => '1990-12-26', 'childClass' => 'A');
// .
// .
// .
$familys[] = array('papaBirthday' => '1982-07-11', 'childClass' => 'C');

// ここから繰り返し実行
foreach ($familys as $listNo => $famiValue) {
  // 誕生日を取得
  $birsMonth = date('m', $famiValue['papaBirthday']);
  $birsDay = date('d', $famiValue['papaBirthday']);
  if ($birsMonth == $sportsMonth && $birsDay == $sportsDay) {
    //今日が誕生日のパパ
    $targetFamily = $famiValue;
    // ここからwhile
    $countPrev = 1;
    while (count($matchingFamilyArr) < 6) {
      // 対象となる家族が、五組になるまで繰り返す。
      if (isset($familys[$listNo - $countPrev])) {
        if ($famiValue['childClass'] == $familys[$listNo - $countPrev]['childClass']) {
          $matchingFamilyArr[] = $familys[$listNo - $countPrev];
        }
      }
      $countPrev++;
    }

    break;
  }
}

今回は、PHPでの事故だったので、PHPで書いてみた。
(そして、サーバーサイドの言語の方が、事故の規模が大きいので・・・。)

while文で、こんな風にすれば、条件に見合う家族が、五組割り出せる。

しかし、気付いた人はいると思うが、
もうこの時点で、無限ループの可能性が大いにある。
39行目のwhile文の条件だと、
対象家族の前に入場した家族の子供と同じクラスの子供が、五組以下なら、
while文は止まらなくなるのだ・・・。

無限ループは、特にPHPやサーバーサイドの言語の場合、
サーバーにタイムアウトの嵐を呼びこみ、とんでもないことになる。

今回は、かなりの時間、ダウンさせてしまった・・・。

これを防ぐとっておきの方法を、上司から教えてもらった。
カウンターをつける、だ。
while文の部分を下記のように直す。

// 上記省略
// 上のスクリプトで定義している変数も使ってるので注意

// ここからwhile
$stopperCount = 0;
$countPrev = 1;
while (count($matchingFamilyArr) < 6 && $stopperCount < 2000 && $stopperCount < count($familys) - (count($familys) - $listNo)) {
  // 対象となる家族が、五組になるまで繰り返す。
  if (isset($familys[$listNo - $countPrev])) {
    if ($famiValue['childClass'] == $familys[$listNo - $countPrev]['childClass']) {
      $matchingFamilyArr[] = $familys[$listNo - $countPrev];
    }
  }
  $countPrev++;
  $stopperCount++;
}

// 下記省略

そう、今回の条件なら、来た家族の組み数以上繰り返す必要はない。
つまり、その数を超えた回数を実行しようとしていたら、それは無限ループが発生しているってことだ。
なら、その回数以上になったら、whileを止める、って仕組みを作ればいい。
また、それプラス、万一のために、2000回を超えないようにしている。
こうすれば、よっぽどのことがない限り、バグはでない。

プログラマーとしては、常識なのかもしれませんが、
全然なってませんでした・・・。
今回はいい勉強になりました。

ほんと気をつけよう・・・。

手軽にホームページを作成【おちゃのこさいさい】 - メイン

トラックバック(0)

トラックバックURL:

コメントする

ページトップへ戻る