Могу ли я улучшить разрешение Thread.Sleep? - PullRequest
8 голосов
/ 30 сентября 2011

Разрешение Thread.Sleep () варьируется от 1 до 15,6 мс

Учитывая это консольное приложение:

class Program
{
    static void Main()
    {
        int outer = 100;
        int inner = 100;
        Stopwatch sw = new Stopwatch();

        for (int j = 0; j < outer; j++)
        {
            int i;
            sw.Restart();
            for (i = 0; i < inner; i++)
                Thread.Sleep(1);
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }
}

Я ожидал, что результат будет 100 чисел, близких к 100. Вместо этого я получаю что-то вроде этого:

99 99 99 100 99 99 99 106 106 99 99 99 100 100 99 99 99 99 101 99 99 99 99 99 101 99 99 99 99 101 99 99 99 100 99 99 99 99 99 103 99 99 99 99 100 99 99 99 99 +813 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1559 1560 1559 1559 1559 1559 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1559 1558 1558 1558 1558 1558

Но иногда я не получаю никаких точных результатов; это будет ~ 1559 каждый раз.

Почему это не соответствует?

Некоторые поиски в Google научили меня, что 15,6 мс - это длина временного интервала, что объясняет результаты ~ 1559. Но почему иногда я получаю правильный результат, а иногда я просто получаю множитель 15,6? (например, Thread.Sleep (20) обычно дает ~ 31,2 мс)

Как на это влияет аппаратное или программное обеспечение?

Я спрашиваю это из-за того, что привело меня к открытию:

Я разрабатывал свое приложение на 32-битной двухъядерной машине. Сегодня моя машина была обновлена ​​до 64-битного четырехъядерного с полной переустановкой ОС. (Windows 7 и .NET 4 в обоих случаях, однако я не могу быть уверен, что на старой машине был установлен W7 SP1; на новой есть.)

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

Почему это поведение изменилось между моей старой системой и моей новой системой? Это связано с аппаратным или программным обеспечением?

Могу ли я сделать это последовательно точно? (Разрешение ~ 1 мс)

Могу ли я что-нибудь сделать в своей программе, чтобы сделать Thread.Sleep () надежно точным с точностью до 1 мс? Или даже 10 мс?

Ответы [ 5 ]

7 голосов
/ 30 сентября 2011

«Функция Sleep приостанавливает выполнение текущего потока на не менее указанного интервала.»

-> http://social.msdn.microsoft.com/Forums/en/clr/thread/facc2b57-9a27-4049-bb32-ef093fbf4c29

7 голосов
/ 30 сентября 2011

Ответ не в том, чтобы использовать Thread.Sleep, а вместо этого использовать таймер высокого разрешения.Вам нужно будет сделать свое затухание в занятом цикле, но, похоже, это не составит проблемы.Вы просто не можете ожидать высокое разрешение от Thread.Sleep, и оно печально известно поведением по-разному на другом оборудовании.

Вы можете использовать класс Stopwatch в .net, который использует производительность высокого разрешениясчетчики, если они поддерживаются на оборудовании.

3 голосов
/ 01 октября 2011

На вашем компьютере запущена программа, которая вызывает timeBeginPeriod () и timeEndPeriod ().Обычно программа, связанная с носителями, которая использует timeSetEvent () для установки таймера в одну миллисекунду.Это также влияет на разрешение Sleep ().

Вы можете сами вызывать эти функции, чтобы получить согласованное поведение.Не очень разумно для эффектов пользовательского интерфейса, хотя.Это довольно недружелюбно для батареи на ноутбуке.

Спать в течение 20 мс и фактически получать 2/64 секунды иначе логично, процессор просто не проснется достаточно скоро, чтобы заметить, что прошло 20 мс.Вы получаете только 1/64 секунды.Таким образом, разумный выбор для таймера, который реализует эффекты затухания, составляет 15 мсек, что дает вам наихудший вариант для анимации 64 к / с.Предполагая, что ваш эффект прорисовывается достаточно быстро.Вы будете немного не в себе, если timeBeginPeriod был вызван, но ненамного.Расчет стадии анимации по часам тоже работает, но в моей книге это немного излишне.

3 голосов
/ 30 сентября 2011

Я могу ответить на один из моих вопросов: Могу ли я сделать его последовательно точным?(Разрешение ~ 1 мс)

Да, кажется, что я могу, используя timeBeginPeriod () и timeEndPeriod ().

Я проверял это и этоработает.

Некоторые вещи, которые я прочитал, предполагают, что вызов timeBeginPeriod (1) для приложения является плохой идеей.Тем не менее, вызывать его в начале короткого метода и затем очищать его с помощью timeEndPeriod () в конце метода должно быть хорошо.

Тем не менее, я также буду исследовать использование таймеров.

0 голосов
/ 30 сентября 2011

Ваш подход неверен.Вы никогда не будете спать, чтобы быть точным, и чем больше ваша машина занята, тем более неправильной будет ваша петля сна.

То, что вы должны делать, это смотреть, сколько прошло времени, и соответственно адаптироваться.Хотя вращение вокруг сна - плохая идея, «лучшие» способы сделать это будут значительно сложнее.Я оставлю предложенное решение простым.

  • Вызовите DateTime.Now.Ticks и сохраните его в переменной (startTick).
  • В своем цикле вызовите Sleep, как вы ужеdo.
  • Вызовите DateTime.Now.Ticks снова и вычтите из него startTick - это значение будет тем, сколько прошло 100 наносекундных единиц времени с момента начала затухания (timeSinceStart).timeSinceStart подсчитайте, сколько вы должны исчезнуть за это время.
  • Повторяйте, пока полностью не исчезнет.
...