2013-05-16 14 views
43

Joe Duffy (autor Concurrent Programming on Windows) pisze w this blog article, że Thread.Sleep (1) jest preferowany w stosunku do Thread.Sleep (0), ponieważ zawiesza się dla wątków o tym samym i niższym priorytecie, a nie tylko dla równych wątków priorytetowych, jak dla Thread.Sleep (0).Czy Thread.Sleep (1) jest wyjątkowe?

Funkcja Thread.Sleep (0) jest wyjątkowa, zawiesza ten wątek i umożliwia wykonanie innych wątków oczekujących. Ale nic nie mówi o Thread.Sleep (1) (dla dowolnej wersji .NET).

Tak, czy Thread.leep (1) rzeczywiście robi coś specjalnego?

Tło:

mam orzeźwiający moją znajomość programowania współbieżnego. Napisałem trochę kodu C#, aby wyraźnie pokazać, że przyrosty i dekrety przed/po są nieatomowe i dlatego nie są bezpieczne dla wątków.

Aby uniknąć konieczności tworzenia setek wątków, umieszczam Thread.Sleep (0) po zwiększeniu wspólnej zmiennej, aby wymusić uruchomienie kolejnego wątku przez program planujący. Ta regularna zamiana nici czyni bardziej oczywistą nieatomową naturę przyrostu/spadku pre/post.

Wygląda na to, że Thread.Sleep (0) nie powoduje dodatkowego opóźnienia, zgodnie z oczekiwaniami. Jednak jeśli zmienię to na Thread.Sleep (1), wydaje się, że powraca do normalnego zachowania podczas snu (np. Otrzymuję z grubsza minimum 1ms opóźnienia).

Oznaczałoby to, że chociaż preferowane może być Thread.Sleep (1), dowolny kod, który używa go w pętli, działałby znacznie wolniej.

To pytanie na temat numeru "Could someone explain this interesting behaviour with Sleep(1)?" jest w pewnym sensie istotne, ale koncentruje się na C++ i po prostu powtarza wskazówki w artykule na blogu Joe Duffy.

Oto mój kod dla wszystkich zainteresowanych (skopiowany z LINQPad, więc może trzeba dodać klasę wokół niego):

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); 
    } 
} 
+3

Thread.Sleep (1) nie jest specjalny w tym sensie, że Sleep (2) lub Sleep (15) zrobią coś zasadniczo innego. Sleep (1) to tylko najniższa wartość wyższa niż Sleep (0), która * jest * specjalna (lub była). – Joren

Odpowiedz

54

Nie trzeba już używać Sleep(1) zamiast Sleep(0) ponieważ Microsoft zmienił implementację Windows API Sleep().

Od the MSDN documentation for Sleep(), to co dzieje się teraz z uśpienia (0):

Wartość zero powoduje wątek zrezygnować z pozostałej jego wycinka czasu do jakikolwiek inny wątek, że jest gotowy do uruchomienia . Jeśli nie ma innych wątków gotowych do uruchomienia, funkcja zwraca natychmiast, a wątek kontynuuje wykonywanie.

to, co zwykło się zdarzyć w systemie Windows XP:

Wartość zero powoduje wątek zrezygnować z pozostałej jego wycinka czasu do dowolny inny wątek równego pierwszeństwa, który jest gotowy do biegać. Jeśli nie ma innych wątków o równym priorytecie gotowych do uruchomienia, funkcja zwraca natychmiast, a wątek kontynuuje wykonywanie. To zachowanie zmieniło się, zaczynając od Windows Server 2003.

Zauważ różnicę między "dowolnym innym wątkiem" a "dowolnym innym wątkiem o równym priorytecie".

Jedynym powodem, dla którego Joe Duffy sugeruje użycie trybu uśpienia (1) zamiast uśpienia (0), jest to, że jest to najkrótsza wartość uśpienia(), która uniemożliwi natychmiastowe zwrócenie stanu wstrzymania(), jeśli nie ma innych nici z równy priorytet gotowy do uruchomienia, podczas pracy w systemie Windows XP.

Nie musisz się tym martwić w przypadku wersji systemu operacyjnego po systemie Windows Server 2003 ze względu na zmianę zachowania funkcji Sleep().

I zwrócić uwagę na tej części bloga Joe:

I chociaż istnieje wyraźna uśpienia tam, wydając nie pozwala producentowi być zaplanowane, bo to na niższym priorytecie.

W XP wątki o niższym priorytecie byłyby głodzone, nawet jeśli głównym wątkiem (o wyższym priorytecie) był tryb uśpienia (0). Post-XP, to już się nie stanie, ponieważ tryb uśpienia (0) pozwala na uruchamianie wątków o niższym priorytecie.

+0

Dobrze wiedzieć, dzięki. Rozumiem, że nie odwołujesz się do dokumentacji MSDN ** ** NET? (strona, do której dołączyłem powyżej). Nie mogę znaleźć tego ważnego tekstu z twojego cytatu. – Ash

+0

Przepraszamy, dodam link do dokumentów MSDN dla Windows API Sleep() ... i gotowe. –

+1

Świetnie !, który nauczy mnie polegać na dokumentach MSDN .NET i artykule z 2006 roku. Zaakceptowana odpowiedź. – Ash