2012-03-15 11 views
52

Czy jest skuteczny doSpinWait vs Sleep waiting. Który użyć?

SpinWait.SpinUntil(() => myPredicate(), 10000) 

na timeout 10000ms

lub

Czy to jest bardziej wydajny w użyciu Thread.Sleep odpytywanie na tych samych warunkach Na przykład coś wzdłuż linii następujących SleepWait funkcja:

public bool SleepWait(int timeOut) 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut) 
    { 
     Thread.Sleep(50) 
    } 
    return myPredicate() 
} 

Jestem przeciw czy wszystkie plony z SpinWait mogą nie być dobrym wzorcem użytkowania, jeśli mówimy o przekroczeniach czasu w ciągu 1 sekundy? Czy to jest prawidłowe założenie?

Które podejście preferujesz i dlaczego? Czy istnieje jeszcze lepsze podejście?


Aktualizacja - Staje się coraz bardziej konkretne:

Czy istnieje sposób, aby BlockingCollection Pulse wątek śpiącego, gdy osiągnie ograniczone zdolności? Raczej unikałem pracowitych czekań, tak jak sugeruje Marc Gravel.

Odpowiedz

39

Podejście najlepiej jest mieć jakiś mechanizm do aktywnie wykrywać rzeczą staje się prawdą (zamiast biernie odpytywania za to mający stać true); może to być dowolny uchwyt oczekiwania, a może Task z Wait, lub może event, który można zapisać, aby się odeprzeć. Oczywiście, jeśli robisz tego rodzaju "poczekaj, aż coś się stanie", to jest to nie tak wydajne, jak po prostu wykonanie następnego kawałka pracy jako callback, co oznacza, że ​​nie musisz używać nici czekać. Task ma ContinueWith do tego, lub możesz po prostu wykonać pracę w event po jej uruchomieniu. event jest prawdopodobnie najprostszym podejściem, w zależności od kontekstu. Task zapewnia jednak już wszystko, o czym tutaj mowa, w tym mechanizmy "wait with timeout" i "callback".

I tak, kręcenie przez 10 sekund nie jest wspaniałe. Jeśli chcesz użyć czegoś takiego jak twój aktualny kod i jeśli masz powody, by spodziewać się krótkiego opóźnienia, ale musisz pozwolić na dłuższe - być może SpinWait przez (powiedzmy) 20 ms, a do reszty użyć Sleep?


Po komentarzu; oto jak bym hak „jest pełna” mechanizm:

private readonly object syncLock = new object(); 
public bool WaitUntilFull(int timeout) { 
    if(CollectionIsFull) return true; // I'm assuming we can call this safely 
    lock(syncLock) { 
     if(CollectionIsFull) return true; 
     return Monitor.Wait(syncLock, timeout); 
    } 
} 

ze w „znalazła się w kolekcji” Kod:

if(CollectionIsFull) { 
    lock(syncLock) { 
     if(CollectionIsFull) { // double-check with the lock 
      Monitor.PulseAll(syncLock); 
     } 
    } 
} 
+0

Dzięki, podoba mi się twoja odpowiedź, to byłoby naprawdę miłe. Załóżmy, że śledzisz wykorzystanie niektórych zasobów za pomocą BlockingCollection. Przyzwyczajają się (i usuwają z kolekcji) i wracają do kolekcji, gdy są ponownie dostępne do ponownego użycia. Zamknięcie może nastąpić tylko wtedy, gdy wszystkie te elementy powrócą do kolekcji. Jak byś poszła o sygnalizowaniu, że zamknięcie może iść naprzód (tzn. Zbieranie znów jest pełne) innym niż pracowite czekanie? – Anastasiosyal

+0

@Anastasiosyal Chciałbym enkapsulować kolekcję i mieć opakowanie zrobić coś, gdy jest pełna; właściwie, prawdopodobnie użyłbym do tego celu 'Monitor'a (doda przykład). –

+0

W destruktorze klasy" ObjectPool ", która pochodzi z BlockingQueue, wyskakuj wszystkie obiekty z BlockingCollection i usuwaj je, (ponieważ jest to C#, ustaw pobraną referencję na zero), dopóki liczba wykasowanych obiektów nie będzie równa głębokości basenu. Wszystkie połączone obiekty zostały następnie usunięte, więc możesz usunąć kolejkę i powrócić z dtor, aby kontynuować sekwencję zamykania. Nie jest wymagany głosowanie/busywait! Być może zechcesz poświęcić trochę czasu na pobranie, aby przeciekany obiekt w końcu spowodował wyjątek (lub coś takiego). –

97

W .NET 4 SpinWait wykonuje CPU-intensywny wirowanie przez 10 iteracji przed plonowaniem. Ale po każdym z tych cykli nie wraca on ponownie do wywołującego ; zamiast tego wywołuje Thread.SpinWait, aby obracać przez CLR (zasadniczo system operacyjny) przez określony czas.Ten okres czasu wynosi początkowo kilka dziesiątek nanosekund, ale podwaja się z każdą iteracją do momentu zakończenia 10 powtórzeń. Umożliwia to przejrzystość/przewidywalność w całkowitym czasie spędzonym na wirowaniu (intensywnie obciążającym procesor), który system może dostroić zgodnie z warunkami (liczba rdzeni itp.). Jeśli SpinWait pozostaje zbyt długo w fazie wirowania, będzie okresowo spać, aby umożliwić kontynuację innych wątków (więcej informacji można znaleźć w artykule J. Albahari's blog). Proces ten gwarantuje utrzymanie rdzeń zajęty ...

Więc SpinWait ogranicza przędzenie procesora intensywnie do określonej liczby powtórzeń, po czym plony jego kawałek czasu na każdym spinie (przez właściwie nazywając Thread.Yield i Thread.Sleep) , zmniejszając zużycie zasobów. Wykryje również, czy użytkownik uruchamia maszynę z jednym rdzeniem i wydajność w każdym cyklu, jeśli tak jest.

Z Thread.Sleep wątek jest zablokowany. Ale proces ten nie będzie tak kosztowny jak powyższe pod względem procesora.

+6

+1 Dziękuję za jasność między SpinWait i thread.Sleep – Anastasiosyal

+0

To jest kopiowane ze strony internetowej Albahari. Należy się do niego odnieść! – georgiosd

+1

Nie jest on kopiowany dosłownie, ale był pod wpływem jego witryny, stąd powołuję się na jego blog. – MoonKnight

Powiązane problemy