14

簡単に言えば、ミリ秒単位の精度で.Netに正確なタイマーが必要です。つまり、10ミリ秒が経過したときにイベントを発生させるように指示した場合、+-1ミリ秒でそうする必要があります。組み込みの .Net Timer クラスの精度は +-16ms のようですが、これは私のアプリケーションでは受け入れられません。

この記事を見つけましたhttp://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timerこれは、まさに必要なものであるタイマーのコードを提供します(さらに-マイクロ秒単位の精度があります) .

ただし、問題は、OnTimer に相当するものが別のスレッドで実行されているように見えることです。したがって、そうするコードを追加すると、次のようになります。

label1.Text = "Hello World";

例外が発生するため、実際には次のように記述する必要があります。

Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));

これは、私が理解していることから、OnTimer イベントがタイマーのスレッドから発生するためです。ここでは、間隔を超えるのに十分な時間が経過するまで時間が経過し、次の OnTimer イベントが発生します。.Net Timer にはそのような問題はありません。.Net Timer の OnTimer では、コントロールのメンバーを自由に変更できます。

質問: タイマーがメイン スレッドで OnTimer イベントを実行するには、何を変更すればよいですか? 「Invoke」を追加するのが唯一の選択肢ですか?

4

3 に答える 3

16

いくつかの方法がありますが、私が一般的に好むのは、タイマーSynchronizationContext.Currentが作成されたときの値をタイマーにキャプチャさせることです。その値には、UI スレッドの場合、UI コンテキストの場合にメッセージ ループでメソッドを実行するために使用できる現在の同期コンテキストが含まれます。これは、winforms、WPF、silverlight などで機能します。これらのパラダイムはすべて、同期コンテキストを設定します。

UIスレッドで作成されたと仮定して、タイマーが作成されたときにその値を取得するだけです。オプションのコンストラクター/プロパティを使用して値を設定し、タイマーが UI スレッドで作成されていなくても使用できるようにしたい場合は、ほとんどの場合必要ありませんが、使用できます。

次にSend、そのコンテキストのメソッドを使用してイベントを発生させます。

public class Timer
{
    private SynchronizationContext syncContext;
    public Timer()
    {
        syncContext = SynchronizationContext.Current;
    }

    public event EventHandler Tick;

    private void OnTick()
    {
        syncContext.Send(state =>
        {
            if (Tick != null)
                Tick(this, EventArgs.Empty);
        }, null);
    }

    //TODO other stuff to actually fire the tick event
}
于 2013-10-21T15:08:56.423 に答える
3

UI Element アクセスをメイン スレッドにディスパッチする方法はありません。UI 要素を更新することが本当にタイマー コールバックで行う唯一のことである場合は、タイマーの精度要件を忘れてください。ユーザーは 16 ミリ秒と 50 ミリ秒の違いを認識しません。

それ以外の場合は、タイマー コールバックでタイム クリティカルな作業を実行し、残りの UI 作業をメイン スレッドにディスパッチします。

void OnTimer()
{
  // time critical stuff here

  Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));
}
于 2013-10-21T15:08:53.530 に答える