0

投稿を削除し、コードを追加しました:

namespace StackOverflowQuestion
{
  class Program
  {
        static void Main (string[] args)
        {
              var cw = new ConsoleWriter ();

              for (int i = 0; i <= 5000000; i++)
              {
                    cw.Write (i);
              }

              Console.ReadKey ();
        }
  }

  class ConsoleWriter
  {
        private Stopwatch sw = new Stopwatch ();

        public ConsoleWriter ()
        {
              sw.Start ();
        }

        public void Write (int pNumber)
        {
              if (sw.ElapsedMilliseconds >= 50)
              {
                    Console.WriteLine (pNumber);
                    sw.Restart ();
              }
        }
  }
}

そして出力:305940
651171
1002965
1358665
1715740
2069602
2419054
2772833
3127880
3485054
3844335
4204016
4557912
4913494

したがって、すべてが正常に機能します。この例では、ConsoleWriterはコンソールに番号を表示しますが、コントロールサーフェスに表示することもできます。ご覧のとおり、Writeメソッドを5000000回呼び出しても、UIは最低50ミリ秒後にしか更新されません。すばらしいですが、多くの場合、最後の値5000000は表示されないことに注意してください。それを修正する方法は?50ミリ秒ごとにイベントを呼び出し、書き込む値が変更されていることを確認するクラス(スレッド)を使用する必要がありますか?

4

2 に答える 2

4

あなたはタイマーを使うことができます

Timer _timer;

public void StartTimer()
{
    _timer = new Timer();
    _timer.Interval = 100; // 100 ms = 0.1 s
    _timer.Tick += new EventHandler(timer_Tick);
    _timer.Start();
}

void timer_Tick(object sender, EventArgs e)
{
    myControl.Number = i;
}

コントロールには次のようなものがあるはずです

private int _number;    
public int Number
{
    get { return _number; }
    set
    {
        if (value != _number) {
            _number = value;
            Invalidate();
        }
    }
}

の呼び出しにより、イベントInvalidate()がトリガーされます。Paintペイントロジックは次のOnPaintメソッドに含まれている必要があります。

protected override void OnPaint(PaintEventArgs e)
{
    ... paint here
}

しかし、原因として、forループ自体がアプリケーションをフリーズします。表示カウンターよりも速い間隔でカウンターを更新する2番目のタイマーを使用できます。UIトレッド(必要に応じてメインスレッド)で実行されているすべてのコードは、終了するまでUIをフリーズします。別のスレッドでバックグラウンドで重い作業を行う簡単な方法は、を使用することBackgroundWorkerです。バックグラウンドワーカーは自動的にUIスレッドとワーカースレッドを切り替え、進行状況をUIスレッドに報告できるようにします。

カウンターを更新するために、手動でスレッドを開始することもできます。これが番号を変更する唯一のスレッドである場合、同期メカニズムは必要ありません。ただし、UIスレッド以外のスレッドからUI(フォームまたはコントロール)にアクセスしないでください。

これは、カウントに別のスレッドを使用した完全なノンブロッキングソリューションです。

Timer _timer;
int _counter;
System.Threading.Thread _worker;

public frmTimerCounter()
{
    InitializeComponent();
    _worker = new System.Threading.Thread(() =>
    {
        while (_counter < 10000000) {
            _counter++;
            System.Threading.Thread.Sleep(20);
        }
    });
    _worker.Start();
    StartTimer();
}

public void StartTimer()
{
    _timer = new System.Windows.Forms.Timer();
    _timer.Interval = 100; // 100 ms = 0.1 s
    _timer.Tick += new EventHandler(timer_Tick);
    _timer.Start();
}

void timer_Tick(object sender, EventArgs e)
{
    // I used a Label for the test. Replace it by your control.
    label1.Text = _counter.ToString();
}
于 2013-01-08T21:20:58.307 に答える
2

あなたはコードを投稿していませんが、私はそれがどのように見えるかを推測することができます。あなたはプロパティセッターであまりにも多くの仕事をしていますこのfor()ループは、1ミリ秒を超えることはなく、GUIのフリーズに気付くには短すぎます。

これは、コントロールが自身を再描画する標準的な方法に従うことで得られます。怠惰です。これは、Invalidate()メソッドを呼び出すことで取得できます。このような:

class MyControl : Control {
    private int number;
    public int Number {
        get { return this.number; }
        set {
            if (value != this.number) this.Invalidate();
            this.number = value;
        }
    }
    protected override void OnPaint(PaintEventArgs e) {
        // TODO: paint number
        //...
        base.OnPaint(e);
    }
}

これで、他の何かも発見できるようになります。そのfor()ループを使用する意味はもうありません。そもそも1つはありませんでした。人間は、現代のプロセッサが数を増やすことができるという信じられないほどの速度をおそらく見ることができません。したがって、これを次のように置き換えます。

myControl.Number = 50000;

あなたが実際に人間の目で数が増えるのを見るつもりなら、あなたはそれをもっと遅くしなければならないでしょう。変化がぼやけに変わるポイントについては、50ミリ秒に1回程度です。それにはタイマーが必要です。

于 2013-01-08T21:37:50.500 に答える