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

Является ли Thread.Sleep(1) особенным?

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

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

Итак, Thread.Sleep(1) действительно делает что-то особенное?

Фон:

Я обновляю свои знания о параллельном программировании. Я написал код С#, чтобы наглядно показать, что приращения и декременты pre/post не являются атомарными и, следовательно, не являются потокобезопасными.

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

Thread.Sleep(0), по-видимому, не вызывает дополнительной задержки, как и ожидалось. Однако, если я изменил это на Thread.Sleep(1), он, похоже, вернется к нормальному режиму сна (например, я получаю примерно минимум 1 мс задержки).

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

Этот вопрос SO "Может ли кто-нибудь объяснить это интересное поведение с помощью Sleep (1)?" является своего рода релевантным, но он сосредоточен на С++ и просто повторяет руководство в Статья Джо Дэффи в блоге.

Здесь мой код для всех, кто интересуется (скопирован из LinqPad, поэтому вам может понадобиться добавить класс вокруг него):

int x = 0;

void Main()
{
    List<Thread> threadList=new List<Thread>();
    Stopwatch sw=new Stopwatch();

    for(int i=0; i<20; i++)
    {
        threadList.Add(new Thread(Go)); 
        threadList[i].Priority=ThreadPriority.Lowest;
    }

    sw.Start();

    foreach (Thread thread in threadList)
    {
        thread.Start();
    } 


    foreach (Thread thread in threadList)
    {
        thread.Join();
    } 

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);

    Thread.Sleep(200);
    Console.WriteLine(x);
}

void Go()
{
    for(int i=0;i<10000;i++)
    {
        x++;
        Thread.Sleep(0);
    }
}
4b9b3361

Ответ 1

Вам больше не нужно использовать Sleep(1) вместо Sleep(0), потому что Microsoft изменила реализацию Windows API Sleep().

Из документация MSDN для Sleep(), вот что происходит сейчас с Sleep (0):

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

Это то, что раньше происходило в Windows XP:

Значение нуля заставляет поток отбрасывать оставшуюся часть своего временного фрагмента на любой другой поток с равным приоритетом, который готов к запуску. Если нет других потоков равного приоритета, готовых к запуску, функция немедленно возвращается, и поток продолжает выполнение. Это поведение изменилось с Windows Server 2003.

Обратите внимание на разницу между "любым другим потоком" и "любым другим потоком с равным приоритетом".

Единственная причина, по которой Джо Даффи предлагает использовать Sleep (1), а не Sleep (0), - это то, что это самое короткое значение Sleep(), которое не позволит немедленному возврату Sleep(), если нет других потоков с равным приоритетом готовый к запуску при работе в Windows XP.

Вам не нужно беспокоиться об этом для версий ОС после Windows Server 2003 из-за изменения поведения Sleep().

Я обращаю ваше внимание на эту часть блога Джо:

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

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