none
プログレスバーのグラヒカルインジケータが真っ黒になりエラーが表示される RRS feed

  • 質問

  • お世話になっております。

    Visual Studio 2005 C#でWindows Formアプリケーションを作っています。

    そのアプリケーションの動作でおかしな現象が出て解決できずに困っております。

    直接C#には関係しないのかもしれませんが、解決策、回避策、ここを尋ねればなど
    ありましたらご教示をお願いいたします。

     [現象]
        プログレスバー表示をスタートから終了までで一回(プログレスバーが閉じる)として、これを12時間~24時間
      連続で実行させると、プログレスバーのグラヒカルインジケータが真っ黒になりエラーが表示される。
        また、エラーは"Attempted to divide by zero"と表示される。

     [質問事項]
        ・OS(又は.NetFramework)の仕様なのでしょうか?
        ・アプリケーション(プログラム)での対応策などはあるのでしょうか?
      ・対策があった場合、その対策が他のOS(Vistaや2000SP4)などに影響を与えることはないのでしょうか?

     [現象確認OS]
      WindowsXP Professional SP2
        WindowsXP Professional SP3

     [アプリケーション開発環境]
        VisualStadio 2005 C# Ver8.0.5
       .NetFramework ver2.0

    2008年10月23日 5:55

回答

  •  nadeshiko さんからの引用
    メモリ管理が怪しいですか。厄介な部分ですね。
    その部分で何かやれることがないか探ってみます。

     

    "閉じる" の部分の実装がどうなっているか提示して頂ければ話は進むかもしれません。

    2008年10月23日 10:34
  • こんにちは!(^^)!ふ~です。

    プログレスバーだけの試験は、簡単に100000回を超えてしまいました。これにフォームの表示、非表示を加え、平均的に動作するように、ガベージコレクション処理を加えると、問題無く動作しております。

    <連続動作可能なソース>

    namespace WinProgressCountTest
    {
        public partial class Form1 : Form
        {
            private int iValue = 0;
            private bool bEnd = false;
            private Progress clsProgress = null;
            private long lCount = 0L; // タイマーカウンター

            public Form1()
            {
                InitializeComponent();

                // タイマーのインターバル時間
                this.tmProgressCount.Interval = 5;
            }

            private void button1_Click(object sender, EventArgs e)
            {

                // タイマースタート
                this.tmProgressCount.Start();

                // プログレスバーの初期化
                this.clsProgress = new Progress();
                this.clsProgress.iProgress = 0;
                this.iValue = 0;
                this.bEnd = false;

                // プログレスバー(Form2)を表示
                this.clsProgress.ShowDialog(this);

            }

            private void timer1_Tick(object sender, EventArgs e)
            {
                // Form2のインスタンスが無い場合
                if (clsProgress == null) return;

                // 処理時間を測定する
                DateTime startTime = DateTime.Now;
               
                // タイマー終了フラグを確認する。
                if (!this.bEnd) {
                    // 進捗が100になったかを確認。
                    if (iValue == 100) {
                        // タイマー終了フラグを変更
                        this.bEnd = true;

                        // Form2を隠す
                        this.clsProgress.Hide();

                        // ガベージコレクションを行う。
                        GC.Collect();

                    } else  {
                        // 進捗が100になっていなければ10インクリメントして
              // プログレスバーに設定する。
                        iValue += 10;
                        this.clsProgress.iProgress = iValue;
                    }
                } else {
                    // プログレスバーをクリアする。
                    this.iValue = 0;
                    this.clsProgress.iProgress = iValue;
                    this.bEnd = false;

                    // Form2を表示する
                    this.clsProgress.Show();
                }

                lCount++;
                Debug.WriteLine("タイマー処理 発生回数=" + lCount.ToString());
                TimeSpan diff = DateTime.Now - startTime;
                Debug.WriteLine("処理時間=" + (diff.Ticks / 10000) + " ms");
                // 使用メモリを表示する
                Debug.WriteLine(System.GC.GetTotalMemory(false).ToString()+" byte");

                // 終了判断条件
                if (100000L < lCount)
                {
                    // タイマースタート
                    this.tmProgressCount.Stop();

                    // Form2クローズする
                    this.clsProgress.Close();

                    Debug.WriteLine("お疲れ様です。無事、耐久試験完了致しました。");
                }
            }
        }
    }

    ◇[まとめ]

    繰り返しに向かない組み合わせ

    1)this.clsProgress.ShowDialog(this)  と this.clsProgress.Hide()

    2)new() と this.clsProgress.ShowDialog(this)  と this.clsProgress.Close()

     

    連続繰り返しに耐える組み合わせ

    1)this.clsProgress.Show()  と this.clsProgress.Hide()   (息継ぎが有る)

    2)this.clsProgress.Show()  と this.clsProgress.Hide()  と GC.Collect()  (平均安定動作)

     

    フォーム2の表示速度も.Show()とHide() が圧倒的に速いです。C#でも、用途にあわせて、メソッドを選ばないと性能がでないようです。ご参考になれば幸いです。

     

     

    2008年10月31日 4:51

すべての返信

  • こんにちは!(^^)!ふ~です。

    連続使用で、メモリ管理の問題と思います。検討には、再現できる簡単なサンプルが必要です。気持はよく分かりますが、残念ながら、技術的な物が見えないと何とも言えません。

     

     

    2008年10月23日 8:30
  • すでに書かれているとおりで、真っ黒になるのはメモリ不足の典型的な症状です。

    で、問題になっていそうなのは、

     nadeshiko さんからの引用

    プログレスバー表示をスタートから終了までで一回(プログレスバーが閉じる)として、これを12時間~24時間

     

    この "閉じる" の実装がどうなっているか、ですね。

    2008年10月23日 8:38
  • !(^^)!ふ~さん。じゃんぬねっとさん。

     

    返信ありがとうございました。


    メモリ管理が怪しいですか。厄介な部分ですね。
    その部分で何かやれることがないか探ってみます。

     

     

    2008年10月23日 9:38
  •  nadeshiko さんからの引用
    メモリ管理が怪しいですか。厄介な部分ですね。
    その部分で何かやれることがないか探ってみます。

     

    "閉じる" の部分の実装がどうなっているか提示して頂ければ話は進むかもしれません。

    2008年10月23日 10:34
  •  !(^^)!ふ~ さんからの引用

     

    連続使用で、メモリ管理の問題と思います。検討には、再現できる簡単なサンプルが必要です。

     

    !(^^)!ふ~さん。じゃんぬねっとさん。
     
    返信ありがとうございました。

     

    再現できる簡単なサンプルを作りました。

    なにかおかしなところがあれば、ご指摘おねがいします。

     

    [動作]
       メインフォーム上のボタンが押されたら、別のフォームで
       100000回プログレスバーを表示するものです。

    [クローズ処理]
       クローズは、タイマー処理部分で"this.clsProgress.Close();"を行っています。

    [フロー]
       Form1:スタートをするためのフォーム
       Form2:プログレスバーを表示するフォーム

       1.Form1のボタン押下でタイマーをスタート
         ↓
       2.プログレスバーの状態や進捗率を初期化し、Form2を表示。
         ↓
       3.タイマー処理にて
       タイマー終了フラグを確認
       タイマー終了フラグがFALSE(未終了)ならば
          進捗率を確認
          ①進捗率が100になったらタイマー終了フラグをTURE(終了)に変更。

          ②進捗率が100になっていなかった場合
           進捗率を10インクリメントして、次のタイマータイミングで再確認。
       タイマー終了フラグがTRUE(終了)ならば
       タイマーを停止し、Form2をクローズする。        
                 ↓
          2に戻る。(For文処理をし続ける。)


    *************************************************************************************************************
    Form1.cs
     public partial class MainForm : Form {

      private int iValue = 0;
      private bool bEnd = false;
      private Progress clsProgress = null;

      public MainForm()
      {
       InitializeComponent();
       /// タイマーのインターバル時間
       this.tmProgressCount.Interval = 5;
      }

      /// <summary>
      /// 表示開始ボタン
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void button1_Click(object sender, EventArgs e) {

       /// ボタン押下後に動きっぱなしにするためFORをを使用しています。
       /// 100000は適当に設定しています。
       for(int iCount = 0; iCount < 100000; iCount++) {
        Thread.Sleep(100);
        if(this.bLoopEnd) {
         break;
        }
        /// タイマースタート
        this.tmProgressCount.Start();
        /// プログレスバーの初期化
        this.clsProgress = new Progress();
        this.clsProgress.iProgress = 0;
        this.iValue = 0;
        this.bEnd = false;
        /// プログレスバー(Form2)を表示
        this.clsProgress.ShowDialog(this);
       }    
      }

      /// <summary>
      /// タイマー処理
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void tmProgressCount_Tick(object sender, EventArgs e) {

       /// タイマー終了フラグを確認する。
       if (!this.bEnd) {
        /// 進捗が100になったかを確認。
        if (iValue == 100) {
         // タイマー終了フラグを変更
         this.bEnd = true;
        } else  {
         /// 進捗が100になっていなければ10インクリメントして
                        /// プログレスバーに設定する。
         iValue += 10;
         this.clsProgress.iProgress = iValue;
        }
       } else {
        /// タイマー処理を終了する。
        this.tmProgressCount.Stop();
        /// プログレスバーを閉じる。
        this.clsProgress.Close();
       }
      }

    ****************************************************************************************************************
    Form2.cs

     public partial class Progress : Form {
      public Progress()
      {
       InitializeComponent();
      }
      
      /// <summary>
      /// プログレスバーの進捗状況を格納します。
      /// </summary>
      public int iProgress{
       set{
        /// Max値を超える場合はMax値に丸める。
        if (value > this.progressBar1.Maximum) {
         this.progressBar1.Value = 100;
        } else {
         this.progressBar1.Value = value;
        }
       }
      }
    ****************************************************************************************************************

     

    2008年10月27日 14:15
  •  nadeshikoさん、こんにちは!(^^)!ふ~です。

    本日の11時に私も試験開始しました。12時~24時ですので、気の長い話です。

     

    Code Snippet

           private void button1_Click(object sender, EventArgs e)
            {
               
                // ボタン押下後に動きっぱなしにするためFORをを使用しています。
                // 100000は適当に設定しています。
                for(int iCount = 0; iCount < 100000; iCount++) {

                    // 処理時間を測定する
                    DateTime startTime = DateTime.Now;

                    Thread.Sleep(100);

                    //if(this.bEnd) {
                    //    break;
                    //}

                    // タイマースタート
                    this.tmProgressCount.Start();

                    // プログレスバーの初期化
                    this.clsProgress = new Progress();
                    this.clsProgress.iProgress = 0;
                    this.iValue = 0;
                    this.bEnd = false;

                    // プログレスバー(Form2)を表示
                    this.clsProgress.ShowDialog(this);

                    TimeSpan diff = DateTime.Now - startTime;
                    Debug.WriteLine("処理時間=" + (diff.Ticks / 10000) + " ms");
                    // 使用メモリを表示する
                    Debug.WriteLine(System.GC.GetTotalMemory(false).ToString()+" byte");
                }   
            }

     

     

    処理時間の変化と、メモリ使用量の変化を表示しながら、デバッグーモードで実験しております。まだ、35分ですが明日どうなっているかが楽しみです。メモリ使用量の変化は650,000バイト~1,550,000バイトの範囲で増減しています。処理時間は328ms一定の速度で表示中です。

     

     

    2008年10月28日 2:42
  • こんにちは!(^^)!ふ~です。

    私のパソコンでも再現出来ました。6時間程で、Form2に貼り付けて有るプログレスバーの色がグリーンより、真っ黒に焼き焦げていました。正確には、プログレスバーのエリア全体が黒です。

    プログラムの方は相変わらず、メモリ使用量650000バイト~1550000バイトの範囲、328msでForm2は表示、クローズを繰り返しております。(他の操作、動作には影響はなし)

    もう少し早期に再現するように、Sleep()を取って実験をしている所です。ご連絡まで

     

    2008年10月29日 4:34
  • おはようございます。!(^^)!ふ~です。

    原因は、仮想タイマーなどのリソースを使い切ってしまい使用出来なくなるようです。連続運転させる為には、タイマーも、Form2も使い続ける事で、内部リソースの消費を抑える事が必要と思われます。

     

    Code Snippet

    <修正版その1:100000回テスト>

            private void button1_Click(object sender, EventArgs e)
            {

                // プログレスバーの初期化
                this.clsProgress = new Progress();

                // タイマースタート
                this.tmProgressCount.Start();

                           
                // ボタン押下後に動きっぱなしにするためFORをを使用しています。
                // 100000は適当に設定しています。
                for(int iCount = 0; iCount < 100000; iCount++) {

     

                    // 処理時間を測定する
                    DateTime startTime = DateTime.Now;

     

                    // プログレスバーの初期化
                    this.clsProgress.iProgress = 0;
                    this.iValue = 0;
                    this.bEnd = false;

     

                    // プログレスバー(Form2)を表示
                    this.clsProgress.ShowDialog(this);

     

                    TimeSpan diff = DateTime.Now - startTime;
                    Debug.WriteLine("処理時間=" + (diff.Ticks / 10000) + " ms");

                    // 使用メモリを表示する
                    Debug.WriteLine(System.GC.GetTotalMemory(false).ToString()+" byte");

                }

                // タイマー処理を終了する。
                this.tmProgressCount.Stop();
            }

     

            private void timer1_Tick(object sender, EventArgs e)
            {

           // clsProgressインスタンスが有るか?

                if (clsProgress == null) return;
               
                // タイマー終了フラグを確認する。
                if (!this.bEnd) {
                    // 進捗が100になったかを確認。
                    if (iValue == 100) {
                        // タイマー終了フラグを変更
                        this.bEnd = true;
                    } else  {
                        // 進捗が100になっていなければ10インクリメントして
                 // プログレスバーに設定する。
                        iValue += 10;
                        this.clsProgress.iProgress = iValue;
                    }
                } else {
                    // プログレスバーを閉じる。
                    this.clsProgress.Hide();
                }
                Debug.WriteLine("タイマー処理 発生");
            }
        }

     

     

    この様な修正を加え、取り合えず、リソース消費に観点を置いて実験中です。明日が楽しみです。

     

     

     

     

     

    2008年10月30日 2:06
  • おはようございます!(^^)!ふ~です。

    どうも、プログレスバーは真っ黒です。残念。this.clsProgress.ShowDialog(this)とthis.clsProgress.Hide()の繰り返しに問題がある(仮定)のでは?

    そこで、Fom2を表示した状態で、プログレスバーのみ連続表示で問題は無い事を確認したいと思います。


    namespace WinProgressCountTest
    {
        public partial class Form1 : Form
        {
            private int iValue = 0;
            private bool bEnd = false;
            private Progress clsProgress = null;
            private long lCount = 0L; // タイマーカウンター

            public Form1()
            {
                InitializeComponent();

                // タイマーのインターバル時間
                this.tmProgressCount.Interval = 5;
            }

            private void button1_Click(object sender, EventArgs e)
            {

                // タイマースタート
                this.tmProgressCount.Start();

                // プログレスバーの初期化
                this.clsProgress = new Progress();
                this.clsProgress.iProgress = 0;
                this.iValue = 0;
                this.bEnd = false;

                // プログレスバー(Form2)を表示
                this.clsProgress.ShowDialog(this);
            }

            private void timer1_Tick(object sender, EventArgs e)
            {
                // Form2のインスタンスが無い場合
                if (clsProgress == null) return;

                // 処理時間を測定する
                DateTime startTime = DateTime.Now;
               
                // タイマー終了フラグを確認する。
                if (!this.bEnd) {
                    // 進捗が100になったかを確認。
                    if (iValue == 100) {
                        // タイマー終了フラグを変更
                        this.bEnd = true;

                    } else  {
                        // 進捗が100になっていなければ10インクリメントして
              // プログレスバーに設定する。
                        iValue += 10;
                        this.clsProgress.iProgress = iValue;
                    }
                } else {
                    // プログレスバーをクリアする。
                    this.iValue = 0;
                    this.clsProgress.iProgress = iValue;
                    this.bEnd = false;
                }
                lCount++;
                Debug.WriteLine("タイマー処理 発生回数=" + lCount.ToString());
                TimeSpan diff = DateTime.Now - startTime;
                Debug.WriteLine("処理時間=" + (diff.Ticks / 10000) + " ms");
                // 使用メモリを表示する
                Debug.WriteLine(System.GC.GetTotalMemory(false).ToString()+" byte");
            }
        }
    }

    また、明日どうなるか楽しみです。

    2008年10月31日 1:48
  • こんにちは!(^^)!ふ~です。

    プログレスバーだけの試験は、簡単に100000回を超えてしまいました。これにフォームの表示、非表示を加え、平均的に動作するように、ガベージコレクション処理を加えると、問題無く動作しております。

    <連続動作可能なソース>

    namespace WinProgressCountTest
    {
        public partial class Form1 : Form
        {
            private int iValue = 0;
            private bool bEnd = false;
            private Progress clsProgress = null;
            private long lCount = 0L; // タイマーカウンター

            public Form1()
            {
                InitializeComponent();

                // タイマーのインターバル時間
                this.tmProgressCount.Interval = 5;
            }

            private void button1_Click(object sender, EventArgs e)
            {

                // タイマースタート
                this.tmProgressCount.Start();

                // プログレスバーの初期化
                this.clsProgress = new Progress();
                this.clsProgress.iProgress = 0;
                this.iValue = 0;
                this.bEnd = false;

                // プログレスバー(Form2)を表示
                this.clsProgress.ShowDialog(this);

            }

            private void timer1_Tick(object sender, EventArgs e)
            {
                // Form2のインスタンスが無い場合
                if (clsProgress == null) return;

                // 処理時間を測定する
                DateTime startTime = DateTime.Now;
               
                // タイマー終了フラグを確認する。
                if (!this.bEnd) {
                    // 進捗が100になったかを確認。
                    if (iValue == 100) {
                        // タイマー終了フラグを変更
                        this.bEnd = true;

                        // Form2を隠す
                        this.clsProgress.Hide();

                        // ガベージコレクションを行う。
                        GC.Collect();

                    } else  {
                        // 進捗が100になっていなければ10インクリメントして
              // プログレスバーに設定する。
                        iValue += 10;
                        this.clsProgress.iProgress = iValue;
                    }
                } else {
                    // プログレスバーをクリアする。
                    this.iValue = 0;
                    this.clsProgress.iProgress = iValue;
                    this.bEnd = false;

                    // Form2を表示する
                    this.clsProgress.Show();
                }

                lCount++;
                Debug.WriteLine("タイマー処理 発生回数=" + lCount.ToString());
                TimeSpan diff = DateTime.Now - startTime;
                Debug.WriteLine("処理時間=" + (diff.Ticks / 10000) + " ms");
                // 使用メモリを表示する
                Debug.WriteLine(System.GC.GetTotalMemory(false).ToString()+" byte");

                // 終了判断条件
                if (100000L < lCount)
                {
                    // タイマースタート
                    this.tmProgressCount.Stop();

                    // Form2クローズする
                    this.clsProgress.Close();

                    Debug.WriteLine("お疲れ様です。無事、耐久試験完了致しました。");
                }
            }
        }
    }

    ◇[まとめ]

    繰り返しに向かない組み合わせ

    1)this.clsProgress.ShowDialog(this)  と this.clsProgress.Hide()

    2)new() と this.clsProgress.ShowDialog(this)  と this.clsProgress.Close()

     

    連続繰り返しに耐える組み合わせ

    1)this.clsProgress.Show()  と this.clsProgress.Hide()   (息継ぎが有る)

    2)this.clsProgress.Show()  と this.clsProgress.Hide()  と GC.Collect()  (平均安定動作)

     

    フォーム2の表示速度も.Show()とHide() が圧倒的に速いです。C#でも、用途にあわせて、メソッドを選ばないと性能がでないようです。ご参考になれば幸いです。

     

     

    2008年10月31日 4:51
  • !(^^)!ふ~さん。

     

    忍耐づよい対応ありがとうございます。

    早速内容を見させていただきます。

    2008年10月31日 15:21
  • こんにちは。中川俊輔 です。

     

    !(^^)!ふ~さん、じゃんぬねっとさん、回答ありがとうございます。

    !(^^)!ふ~さんは長時間にわたる検証をしてくださったようですね!

     

    nadeshikoさん、フォーラムのご利用ありがとうございます。

    有用な情報と思われたため、

    !(^^)!ふ~さん、じゃんぬねっとさんの回答へ回答済みチェックをつけさせていただきました。

     

    今後ともフォーラムをよろしくお願いします。

    それでは!

    2008年11月14日 6:58