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

Почему Thread.Sleep настолько вреден

Я часто вижу, что он упоминал, что 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);
    }
}
4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

Мой ответ в основном охватывает прецедент 2

Ответ 2

SCENARIO 1 - дождитесь завершения задачи async: я согласен, что WaitHandle/Auto | ManualResetEvent следует использовать в сценарии, когда поток ожидает выполнения задачи в другом потоке.

SCENARIO 2 - синхронизация во время цикла. Однако, поскольку механизм сырой синхронизации (в то время как + Thread.Sleep) отлично подходит для 99% приложений, которые НЕ требуют точно знать, когда заблокированный поток должен "просыпаться". что требуется 200k циклов для создания потока, также является недопустимым - поток циклов синхронизации должен быть создан в любом случае, а 200k циклов - просто еще одно большое число (скажите, сколько циклов открывает вызовы file/socket/db?).

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

Ответ 3

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

Несколько моих других фаворитов в мире С# - это то, что они говорят вам: "никогда не вызывайте блокировку (это)" или "никогда не вызывайте GC.Collect()". Эти два человека объявлены во многих блогах и официальной документации, а ИМО - полная дезинформация. На некотором уровне эта дезинформация служит своей цели, поскольку она удерживает новичков от того, что они не понимают, прежде чем полностью изучать альтернативы, но в то же время затрудняет поиск информации REAL с помощью поисковых систем, что все похоже, указывают на статьи, в которых вам нечего делать, не предлагая ответа на вопрос "почему бы и нет?"

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

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

Ответ 4

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

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

while(inWait)Thread.Sleep(5000); 

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

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

Как упоминал Андреас Н., прочитайте Threading in С#, автор Joe Albahari, это действительно действительно хорошо.

Ответ 5

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

Ответ 6

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

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

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 на ней.

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

Ответ 7

Для тех из вас, кто не видел одного действительного аргумента против использования Thread.Sleep в SCENARIO 2, действительно есть один выход приложения, который будет удерживаться циклом while (SCENARIO 1/3 просто глупо, поэтому не стоит больше упоминать)

Многие, кто претендует на то, чтобы быть в курсе Thread.Sleep, злой не упомянул ни одну действительную причину для тех из нас, кто требовал практической причины не использовать его, - но здесь это, благодаря Питу - 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;
    }