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

К моему пониманию Потока. Сон (0) вызывает контекстное переключение на ОС.

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

Таким образом, я создал приложение, которое действительно Распараллеливает. Сон (0) в некоторое время цикле (c#) и вычисляет время, которые передают между каждым вызовом.

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

Когда я выполняю его вдоль ЦП, Заполняющего фиктивное приложение (все с тем же приоритетом), макс. время составляет приблизительно 25 мс, и среднее время составляет 20 мс. Это ведет себя точно как, я ожидаю это. И время очень стабильно.

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

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

Я упускаю некоторую важную суть относительно Потока. Сон (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 мс поздно).

24
задан Chris Schiffhauer 25 February 2015 в 17:08
поделиться

5 ответов

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

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

33
ответ дан 28 November 2019 в 23:25
поделиться

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

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

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

6
ответ дан 28 November 2019 в 23:25
поделиться

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

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

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

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

3
ответ дан 28 November 2019 в 23:25
поделиться

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

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

3
ответ дан 28 November 2019 в 23:25
поделиться

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

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

0
ответ дан 28 November 2019 в 23:25
поделиться
Другие вопросы по тегам:

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