Почему Thread.Sleep так вредно - PullRequest
110 голосов
/ 11 января 2012

Я часто вижу упоминание о том, что Thread.Sleep(); не следует использовать, но я не могу понять, почему это так.Если Thread.Sleep(); может вызвать проблемы, есть ли какие-нибудь альтернативные решения с таким же результатом, которые были бы безопасны?

например.

while(true)
{
    doSomework();
    i++;
    Thread.Sleep(5000);
}

другое:

while (true)
{
    string[] images = Directory.GetFiles(@"C:\Dir", "*.png");

    foreach (string image in images)
    {
        this.Invoke(() => this.Enabled = true);
        pictureBox1.Image = new Bitmap(image);
        Thread.Sleep(1000);
    }
}

Ответы [ 7 ]

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

Проблемы с вызовом Thread.Sleep объясняются довольно кратко здесь :

Thread.Sleep имеет свое применение: моделирование длительных операций во время тестирования / отладки в потоке MTA. В .NET нет другой причины использовать его.

Thread.Sleep(n) означает заблокировать текущий поток хотя бы на номер временных интервалов (или квантов потока), которые могут встречаться в пределах n миллисекунды. Длина временного интервала различна в разных версиях / типах Windows и разные процессоры и обычно колеблется от 15 до 30 миллисекунды. Это означает, что поток почти гарантированно заблокирован для более n миллисекунд. Вероятность того, что ваша нить будет повторное пробуждение ровно через n миллисекунд практически невозможно невозможно может быть. Итак, Thread.Sleep бессмысленно для хронометража .

Потоки являются ограниченным ресурсом, они занимают приблизительно 200 000 циклов создать и около 100 000 циклов уничтожить. По умолчанию они зарезервируйте 1 мегабайт виртуальной памяти для своего стека и используйте 2000-8000 циклы для каждого переключения контекста. Это делает любой ожидающий поток Огромные отходы.

Предпочтительное решение: WaitHandles

Самая большая ошибка - использование Thread.Sleep с конструкцией while ( демонстрация и ответ , хорошая запись в блоге )

EDIT:
Я хотел бы улучшить свой ответ:

У нас есть 2 разных варианта использования:

  1. Мы ждем, потому что мы знаем определенный промежуток времени, когда мы должны продолжить (используйте Thread.Sleep, System.Threading.Timer или аналогичные)

  2. Мы ждем, потому что какое-то условие меняется со временем ... Ключевое / ые слово / а какое-то время ! если проверка условий находится в нашем кодовом домене, мы следует использовать WaitHandles - в противном случае внешний компонент должен предоставить какие-то крючки ... если это не так, его дизайн плохой!

Мой ответ в основном касается варианта использования 2

32 голосов
/ 18 февраля 2013

СЦЕНАРИЙ 1 - ожидание завершения асинхронной задачи: я согласен, что WaitHandle / Auto | ManualResetEvent следует использовать в сценарии, где поток ожидает выполнения задачи в другом потоке.

СЦЕНАРИЙ 2 - синхронизация во время цикла: Тем не менее, как грубый механизм синхронизации (while + Thread.Sleep) идеально подходит для 99% приложений, которым НЕ требуется знать точно , когда заблокированная нить должна "проснуться"Аргумент о том, что для создания потока требуется 200 тыс. Циклов, также недопустим - поток цикла синхронизации необходимо создать в любом случае, а 200 тыс. Циклов - это просто еще одно большое число (скажите, сколько циклов нужно открыть для вызова файла / сокета / базы данных?).

Так что если while + Thread.Sleep работает, зачем все усложнять? Только юристы по синтаксису будут практичными !

7 голосов
/ 11 августа 2016

Я хотел бы ответить на этот вопрос с точки зрения политики кодирования, которая может или не может быть полезна кому-либо. Но особенно когда вы имеете дело с инструментами, предназначенными для 9-5 корпоративных программистов, люди, которые пишут документацию, обычно используют слова типа «не должен» и «никогда», чтобы означать «не делайте этого, если вы действительно не знаете, что вы делаешь и почему ".

Несколько других моих любимых в мире C # слов: «никогда не вызывайте lock (this)» или «никогда не вызывайте GC.Collect ()». Эти два факта принудительно объявлены во многих блогах и официальной документации, и IMO - полная дезинформация. На некотором уровне эта дезинформация служит своей цели, поскольку она удерживает новичков от выполнения вещей, которые они не понимают, прежде чем полностью исследовать альтернативы, но в то же время, это затрудняет поиск РЕАЛЬНОЙ информации через поисковые системы, которые все кажется, указывают на статьи, в которых говорится, что вы ничего не должны делать, не предлагая ответа на вопрос «почему бы и нет?»

Политически это сводится к тому, что люди считают «хорошим дизайном» или «плохим дизайном». Официальная документация не должна диктовать дизайн моего приложения. Если есть действительно техническая причина, по которой вы не должны вызывать sleep (), то в документации IMO должно быть указано, что вполне нормально вызывать ее при определенных сценариях, но, возможно, предложите некоторые альтернативные решения, которые не зависят от сценария или более подходят для других. сценарии.

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

3 голосов
/ 22 июля 2015

Sleep используется в тех случаях, когда независимые программы, которые вы не можете контролировать, могут иногда использовать обычно используемый ресурс (скажем, файл), к которому ваша программа должна обращаться при запуске и когда ресурс находится в использовать эти другие программы ваша программа заблокирована от его использования. В этом случае, когда вы обращаетесь к ресурсу в своем коде, вы помещаете свой доступ к ресурсу в try-catch (чтобы поймать исключение, когда вы не можете получить доступ к ресурсу), и вы помещаете это в цикл while. Если ресурс бесплатный, сон никогда не вызывается. Но если ресурс заблокирован, то вы спите в течение соответствующего времени и пытаетесь снова получить доступ к ресурсу (вот почему вы зацикливаетесь). Однако имейте в виду, что вы должны установить какой-то ограничитель на цикл, так что это не потенциально бесконечный цикл. Вы можете установить в качестве условия ограничения N попыток (это то, что я обычно использую) или проверить системные часы, добавить фиксированное количество времени, чтобы получить ограничение по времени, и прекратить попытки доступа, если вы достигнете ограничения по времени.

3 голосов
/ 20 марта 2015

Это цикл 1) .spinning и 2) .polling ваших примеров, который люди предостерегают от , а не от Thread.Sleep ().Я думаю, что Thread.Sleep () обычно добавляется, чтобы легко улучшить код, который вращается или находится в цикле опроса, поэтому он просто связан с «плохим» кодом.

Кроме того, люди делают такие вещи, как:

while(inWait)Thread.Sleep(5000); 

, где переменная inWait не доступна потокобезопасным способом, что также вызывает проблемы.

То, что программисты хотят видеть, это потоки, управляемые конструкциями Events и Signaling и Locking, и когда вы это сделаете, вам не понадобится Thread.Sleep (), а также проблемы с безопасным потоком доступом к переменнымтакже устранены.Например, не могли бы вы создать обработчик событий, связанный с классом FileSystemWatcher, и использовать событие для запуска второго примера вместо зацикливания?

Как упомянул Андреас Н., прочитайте Threading in C # Джо Албахари , это действительно очень хорошо.

0 голосов
/ 01 сентября 2015

Я согласен со многими здесь, но я также думаю, что это зависит.

Недавно я сделал этот код:

private void animate(FlowLayoutPanel element, int start, int end)
{
    bool asc = end > start;
    element.Show();
    while (start != end) {
        start += asc ? 1 : -1;
        element.Height = start;
        Thread.Sleep(1);
    }
    if (!asc)
    {
        element.Hide();
    }
    element.Focus();
}

Это была простая анимационная функция, и я использовал Thread.Sleep на нем.

Мой вывод, если он выполняет свою работу, используйте его.

0 голосов
/ 20 февраля 2013

Для тех из вас, кто не видел ни одного действительного аргумента против использования Thread.Sleep в SCENARIO 2, действительно есть один - выход из приложения будет задерживаться циклом while (СЦЕНАРИЙ 1/3 просто глупый, поэтому не заслуживает упоминания)

Многие из тех, кто притворяется, что они в курсе, кричат ​​«Нить». «Сон - зло», не упомянул ни одной веской причины для тех из нас, кто требовал практической причины не использовать его - но вот, благодаря Питу, - Thread.Sleep is Evil (можно легко избежать с помощью таймера / обработчика)

    static void Main(string[] args)
    {
        Thread t = new Thread(new ThreadStart(ThreadFunc));
        t.Start();

        Console.WriteLine("Hit any key to exit.");
        Console.ReadLine();

        Console.WriteLine("App exiting");
        return;
    }

    static void ThreadFunc()
    {
        int i=0;
        try
        {
            while (true)
            {
                Console.WriteLine(Thread.CurrentThread.ThreadState.ToString() + " " + i);

                Thread.Sleep(1000 * 10);
                i++;
            }
        }
        finally
        {
            Console.WriteLine("Exiting while loop");
        }
        return;
    }
...