Синхронизация Forms.Timer и Diagnostics.Stopwatch - PullRequest
3 голосов
/ 11 января 2012

У меня есть функция (скажем, foo()), которая будет вызываться время от времени с переменным интервалом. Когда он вызывается, он проверяет время и предпринимает соответствующее действие.

Я сделал это следующим образом:

  • A Forms.Timer объект вызывает функцию, когда требуется

  • Объект Diagnostics.Stopwatch используется внутри функции с целью определения времени и решения, что делать.

Однако у меня возникает следующая проблема: когда по обратному вызову Timer вызывается foo(), значение ElapsedMilliseconds объекта секундомера обычно ниже ожидаемого. Например, таймер установлен на 1000, поэтому после 1000 мс вызывается foo(), но в пределах foo() body ElapsedMilliseconds возвращает 900, поэтому foo ведет себя так, как будто истекшее время было 900 (хотя оно должно выполнить действие A, поскольку 1000 мс фактически прошло, это не так)

Как я могу синхронизировать таймер и секундомер в том случае, если 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 возвращает более раннее значение, чем утверждения таймера, хотя интервал закончен, следующий элемент не рисуется

Ответы [ 2 ]

3 голосов
/ 11 января 2012

Вы получаете большую разницу, потому что вы запускаете таймер где-то в интервале 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 поздно, это полностью зависит от отзывчивости вашего потока пользовательского интерфейса.Однако всегда поздно.Не используйте этот код, напишите свой код, чтобы вам было все равно, что эти таймеры не синхронизированы.Возможно, вам придется сократить интервал таймера, чтобы достичь этого, это не ясно из вопроса.

2 голосов
/ 11 января 2012

Вы не добьетесь большого успеха с этим подходом.Вы не запускаете каждый таймер в одно и то же время и не проверяете их в одно и то же время (между таймером, запускающим его событие, и вашим кодом запрашивается секундомер).

Выберите один таймер и основывайте все на нем, если хотите, чтобы все было синхронизировано.Например, если вы хотите использовать 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();
    }
...