Вопрос по thread-sleep, .net – Могу ли я улучшить разрешение Thread.Sleep?

8

Разрешение 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 1559 1559 1559 1559 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1558 1559

Но иногда я не получаю никаких точных результатов; это будет ~ 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 мс?

@Henk. Сон имеет свое применение. Erix
Настоящая программа не должна Sleep () в любом случае. Но стандартные таймеры имеют такое же разрешение. Henk Holterman
Мне понравился вопрос так, как я его написал. Пожалуйста, воздержитесь от внесения изменений, которые явно не исправляют ошибку. Igby Largeman
Заставить читателей нажимать ненужные PageDowns для совершенно бессмысленных данных - ошибка. Henk Holterman
Может быть, какой-то другой процесс или сама ОС часто делаютtimeBeginPeriod(1)/timeEndPeriod(1). Igby Largeman

Ваш Ответ

5   ответов
7

по крайней мере указанный интервал. »

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

Хотя это связано, это не отвечает ни на один из моих вопросов. Igby Largeman
Фраза «по крайней мере» отвечает на ваш прямой вопрос (с «Нет»). Henk Holterman
3

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

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

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

Это правда, 16 мс достаточно для этой конкретной ситуации. Резкое увеличение произошло из-за того, что метод работал с циклом 100 раз, изменяя непрозрачность с 1 до 0 с шагом 1%. Таким образом, 100 * 3 = 0,3 секунды стало 100 * 15,6 = 1,6 секунды. Поэтому я могу либо использовать timeBeginPeriod / timeEndPeriod, либо переработать цикл так, чтобы он только зацикливался (исчезать в миллисекундах / 16) раз и изменял непрозрачность с шагом (1 / число циклов). Я пробовал оба метода, и они оба работают хорошо. (Или я могу использовать таймер высокого разрешения) Igby Largeman
Ну и дела, Ганс, ты мог бы сказать мне раньше! Я не работаю в этой компании и даже не живу в этой стране! Это действительно работало хорошо, хотя. Я просто потушил форму примерно за полсекунды. Igby Largeman
Отметил благодарности. В конце я использовал StopWatch и просто зацикливался, проверяя MillisecondsElapsed; этот метод с точностью до миллисекунды. В этом случае можно есть циклы. Igby Largeman
Ну, как я уже говорил, не парься по мелочам. Но не используйте 16, это слишком долго и даст вам 2/64 или 0,016 секунды. Используйте 15, получите 1/64 или 0,015 секунды. Hans Passant
Это плохая идея. Ваша анимация будет работать намного плавнее, когда вы спите или используете таймер. Когда вы записываете циклы, как и вы, планировщик потоков помещает вас в собачью будку на некоторое время после того, как вы прожигаете квант, чтобы дать другим потокам в других процессах возможность работать. Hans Passant
3

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

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

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

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

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

7

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

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

Мое единственное предположение, что какой-то другой процесс, или сама ОС, делаетtimeBeginPeriod(1)/timeEndPeriod(1). Igby Largeman
Но я все еще хотел бы, чтобы кто-то мог сказать мне, что происходит - почему это переключается между разрешением 1 мс и 15 мс, казалось бы, наугад. Igby Largeman
Я пошел с этим решением. Я получил точность в миллисекунды, сидя в цикле при проверке StopWatch.MillisecondsElapsed. Как вы сказали, в этом случае хорошо работает цикл занятости. Igby Largeman
-1

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

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

Вызовите DateTime.Now.Ticks и сохраните его в переменной (startTick).В своем цикле, вызовите Sleep, как вы уже делаете.Снова вызовите DateTime.Now.Ticks и вычтите из него startTick - это значение будет тем, сколько прошло 100 наносекундных единиц времени с момента начала затухания (timeSinceStart).Используя timeSinceStart, вычислите, сколько вы должны потерять за это время.Повторяйте, пока вы полностью не исчезли.
Вы очень не правы. Используйте секундомер. Секундомер не совпадает с DateTime.Now. StopWatches имеют высокое разрешение и представляют реальные тики, DateTime.Now Ticks - низкое разрешение и обновляются только каждые 1/64 секунды. Существует бесконечная информация об этом, если вы ищете это. Tim Lloyd
Если я вызову Sleep in the loop, как уже делаю, как это повлияет? Igby Largeman
-1 DateTime Ticks имеет те же проблемы, что и сейчас, связана с разрешением системных часов, например, 1 \ 64 секунды. Tim Lloyd
Та же самая схема применяется для всего, что связано с синхронизацией - вычисление того, сколько времени прошло. Скажем, вы собираетесь обновлять отображение в КБ / с каждую секунду, ожидаете ли вы, что ваш код обновления будет вызываться идеально каждую секунду? Я гарантирую, что не будет. Вы пытаетесь обновлять его каждую секунду, и ваш алгоритм вычисляет, сколько времени фактически прошло и сколько байтов было передано при расчете КБ / с. Doug65536
Вы используете DateTime.Now.Ticks, чтобы отследить, сколько времени сон спал. Он что-то угасает, ему не понадобится сверхвысокое время. По сути, его проблема в том, что сон может спать дольше, чем требуется - мое решение состоит в том, чтобы посчитать, сколько времени он на самом деле спал, чтобы он работал правильно, когда система находится под нагрузкой. Почему так важно, что он не может спать в течение очень точного периода времени - примите факт, что вы можете не иметь 100-процентного контроля над процессором, и сон не обязательно будет спать в нужном количестве. Мой алгоритм корректно исчезает в зависимости от прошедшего времени. Doug65536

Похожие вопросы