Подтвердить что ты не робот

Thread.Sleep(0): Каково нормальное поведение?

Насколько я понимаю, Thread.Sleep(0) заставляет контекстный переключатель в ОС.

Я хотел проверить, какое максимальное время, которое может пройти в приложении, чтобы получить некоторое время процессора.

Итак, я создал приложение, которое делает Thread.Sleep(0) в цикле while (С#) и вычисляет время, которое проходит между каждым вызовом.

Когда это приложение является единственным, работающим на двухъядерном тестовом ПК, максимальное наблюдаемое время составляет менее 1 миллисекунды (со средним значением 0,9 микросекунды), и оно использует весь доступный центральный процессор (100%).

Когда я запускаю его вместе с приложением для заполнения процессора (все с одинаковым приоритетом), максимальное время составляет около 25 мс, а среднее время составляет 20 мс. Он ведет себя точно так, как я ожидаю. И время очень стабильное.

Всякий раз, когда он получает некоторое процессорное время, он сразу же возвращает управление тому, кто имеет какую-то обработку, он похож на горячую картофельную игру (загрузка процессора падает до 0%). Если при этом не запускается другое приложение, управление немедленно возвращается.

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

Я пропустил какой-то важный момент в отношении Thread.Sleep(0)?

В качестве ссылки здесь приведен код этого приложения

private bool _running = true;
private readonly Stopwatch _timer = new Stopwatch();

private double _maxTime;
private long _count;
private double _average;
private double _current;

public Form1()
{
    InitializeComponent();
    Thread t = new Thread(Run);
    t.Start();
}

public void Run()
{
    while(_running)
    {
        _timer.Start();
        Thread.Sleep(0);
        _timer.Stop();

        _current = _timer.Elapsed.TotalMilliseconds;
        _timer.Reset();
        _count++;

        _average = _average*((_count - 1.0)/_count) + _current*(1.0/_count);
        if(_current>_maxTime)
        {
            _maxTime = _current;
        }
    }
}

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

Я хотел проверить, какова текущая изменчивость, вызванная другим процессом на той же машине: я утверждаю, что, приложив приложение, написанное выше на этой машине в режиме реального времени, максимальное наблюдаемое время скажет мне, какая изменчивость вызвана система. И.Е. У меня есть 300 мс, но максимальное наблюдаемое время до того, как поток получает некоторое время процессора, составляет 50 мс, поэтому для повышения производительности я должен установить время обработки на максимум 250 мс (так как я мог уже опоздать на 50 мс).

4b9b3361

Ответ 1

Он не заставляет контекстный переключатель, только Sleep (1) делает это. Но если есть какой-либо другой поток из любого процесса, готовый к запуску и имеющий более высокий приоритет, тогда Sleep (0) даст процессор и позволит ему работать. Вы можете увидеть это, запустив бесконечный цикл, который вызывает Sleep (0), он будет записывать 100% циклов процессора на одном ядре. Я не понимаю, почему вы не наблюдаете этого поведения.

Лучший способ сохранить отзывчивость системы - это дать вашему потоку низкий приоритет.

Ответ 2

Я был укушен этой ошибкой в ​​предыдущем проекте. У меня был поток, который проверял сообщения в очереди с приоритетом, ища новую. Если он не нашел нового сообщения, я хотел, чтобы поток спал до тех пор, пока сообщение, добавляемое в очередь, не разбудит его, чтобы снова проверить.

Наивно, предполагая, что Thread.Sleep(0) приведет к тому, что поток перейдет в спящий режим до тех пор, пока не проснется снова, я обнаружил, что наше приложение потребляет сумасшедшие объемы процессора, когда сообщения начали поступать.

Через несколько дней после устранения возможных причин мы обнаружили информацию от эту ссылку. Быстрое исправление заключалось в использовании Thread.Sleep(1). Ссылка содержит информацию о причине разницы, включая небольшое тестовое приложение внизу, демонстрируя, что происходит с производительностью между двумя параметрами.

Ответ 3

Скорее всего, причина в том, что вы не позволяете программе правильно читать инструкции.

При вызове Sleep(0) ваш код приостанавливается на один цикл, а затем на следующий доступный слот. Однако это переключение контекста не является бесплатным - для сохранения/загрузки существует множество регистров, и когда у вас есть другая последовательность инструкций для чтения с диска, вы, вероятно, в конечном итоге получите немало промахов в кеше. Я не могу себе представить, что это в значительной степени влияет на ваше приложение, но если вы работаете с системой реального времени или чем-то подобным образом интенсивным, это может быть возможно.

Ответ 4

Мое понимание заключается в том, что Thread.Sleep(0) не форсирует переключатель контекста потока, он просто сигнализирует планировщику задач, что вы готовы отказаться от остальной части вашего среза времени, если есть другие потоки, ожидающие выполнения.

Ваша петля вокруг Sleep (0) пережевывает процессорное время, и это будет отрицательно влиять на другие приложения (и время автономной работы ноутбука!). Sleep (0) не означает "пусть все остальное выполняется первым", поэтому ваш цикл будет конкурировать за время выполнения с другими процессами.

Передача ненулевого времени ожидания для Sleep() будет незначительно лучше для других приложений, потому что это фактически заставило бы этот поток отложить в течение минимального промежутка времени. Но это все равно не то, как вы реализуете поток фона с минимальным воздействием.

Лучший способ запустить связанный с процессором фоновый поток с минимальным воздействием на приложения переднего плана - снизить приоритет вашего потока до уровня ниже обычного. Это покажет планировщику выполнение всех потоков нормального приоритета в первую очередь, и если/когда есть какое-либо другое доступное время, выполните свой низкоприоритетный поток. Побочным эффектом этого является то, что иногда ваш поток с низким приоритетом может не получить какое-либо время выполнения вообще в течение относительно продолжительных периодов времени (в секундах) в зависимости от того, насколько насыщен процессор.

Ответ 5

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

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