3

foo()可変間隔で時々呼び出される関数(たとえば)があります。呼び出されると、時刻を確認し、それに応じてアクションを実行します。

私は次の方法でこれを行いました:

  • 必要に応じてオブジェクトがForms.Timer関数を呼び出す

  • Diagnostics.Stopwatchオブジェクトは、時間を決定し、何をすべきかを決定する目的で関数内で使用されます。

ただし、次の問題があります。foo()タイマーのコールバックによって呼び出されると、ElapsedMilliseconds通常、ストップウォッチ オブジェクトの値が予想よりも低くなります。たとえば、タイマーが 1000 に設定されているため、1000 ミリ秒後にfoo()呼び出されますが、foo()ボディ内で 900 をElapsedMilliseconds返すfooと、経過時間が 900 であるかのように動作します (ただし、実際には 1000 ミリ秒が経過したため、アクション A を実行する必要があります)。

ElapsedMilliseconds がタイマーと一貫した値を持つような場合、タイマーとストップウォッチを同期するにはどうすればよいですか?

編集:いくつかのコード

私の問題を説明するサンプルコード:

//foo is the function that is called by timer's callback
public void foo()
{
    //Let's see what time it is: 
    long currentTime = stopwatch.ElapsedMilliseconds();
    Item = getCurrentItem(currentTime);
    Item.Draw();
}

//this is the callback of timer
private void timer1_Tick(object sender, EventArgs e)
    {
        //set the timer for next time
        timer1.Interval = Intervals[periodIdx++];
        foo();
    }

これは、間隔が完了するたびに何かを描画することになっていますがElapsedMilliseconds、タイマーが要求するよりも早い値を返すため、間隔が終了しても次のアイテムは描画されません

4

2 に答える 2

3

1/64秒間隔のどこかでタイマーを開始するため、大きな違いがあります。これでより良い結果が得られます:

    private void StartTimers() {
        int tick = Environment.TickCount;
        while (Environment.TickCount == tick) Thread.Sleep(0);
        timer1.Enabled = true;
        stopwatch.Start();
    }

while()ループは、1/64タイマーティックの開始時にタイマーが開始される確率を向上させます。改善するだけで、保証はありません。また、Tickイベントの遅延発生については何もできません。これは、UIスレッドの応答性に完全に依存します。しかし、それは常に遅いです。このコードを使用しないでください。これらのタイマーが同期していないことを気にしないようにコードを記述してください。それを達成するためにタイマーの間隔を減らす必要があるかもしれません、それは質問から明らかではありません。

于 2012-01-11T16:32:05.707 に答える
2

このアプローチでは、多くの成功を収めることはできません。各タイマーをまったく同時に開始しておらず、まったく同時にチェックしていません(タイマーがイベントを発生させてからコードがストップウォッチを照会するまでに時間の経過があります)。

1 つのタイマーを選択し、すべてを同期させたい場合は、それをベースにします。たとえば、Forms.Timer を使用する場合は、イベント ハンドラーでカウンター変数をインクリメントするだけです。これにより、ハンドラーが呼び出された回数と、実質的に Forms.Timer が言う時間を知ることができます。過ぎました。例を次に示します (カウンターが long.MaxValue を超えるのに十分な長さのタイマーの刻みのケースを処理するのはあなたに任せます)。

public void foo()
{
    Item = getCurrentItem(totalElapsed);
    Item.Draw();
}

long totalElapsed = 0;

private void timer1_Tick(object sender, EventArgs e)
    {
        totalElapsed += timer1.Interval;
        //set the timer for next time
        timer1.Interval = Intervals[periodIdx++];
        foo();
    }
于 2012-01-11T15:59:18.757 に答える