Dlaczego wątek.Sen tak szkodliwy

Często widzę wzmiankę, że Thread.Sleep(); nie powinny być używane, ale nie mogę zrozumieć, dlaczego tak jest. Jeśli Thread.Sleep(); może powodować problemy, czy są jakieś alternatywne rozwiązania o tym samym wyniku, które byłyby bezpieczne?

Np.

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

Kolejny to:

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);
    }
}
Author: davmos, 2012-01-11

7 answers

Problemy z wywołaniem Thread.Sleepwyjaśnione dość zwięźle tutaj :

Thread.Sleep ma swoje zastosowanie: symulowanie długich operacji podczas testowania / debugowania na wątku MTA. W. Net nie ma innego powodu, aby go używać.

Thread.Sleep(n) oznacza blokowanie bieżącego wątku dla co najmniej liczby z timeslices (lub thread quantums), które mogą wystąpić w obrębie n milisekundy. Długość czasu jest różna w różnych wersjach/rodzajach Okna i różnych procesorów i ogólnie waha się od 15 do 30 milisekundy. Oznacza to, że wątek jest prawie gwarantowany, aby zablokować dla więcej niż n milisekundy. Prawdopodobieństwo, że Twój wątek będzie re-awaken dokładnie po n milisekundach jest tak samo niemożliwe jak niemożliwe może być. więc, {[0] } jest bezcelowe dla czasu.

Wątki są ograniczonym zasobem, zajmują około 200 000 cykli do tworzenia i około 100 000 cykli do zniszczenia. Domyślnie są zarezerwuj 1 megabajt pamięci wirtualnej na swój stos i użyj 2000-8000 cykle dla każdego przełącznika kontekstowego. to sprawia, że każdy czekający wątek a ogromne odpady.

Preferowane rozwiązanie: WaitHandles

Najczęściej popełnianym błędem jest użycie Thread.Sleep z while-construct (demo i odpowiedź, fajny blog-wpis )

EDIT:
Chciałbym poprawić swoją odpowiedź:

Mamy 2 różne przypadki użycia:

  1. Czekamy, bo wiemy, że określony czas, kiedy powinniśmy kontynuować (użyj Thread.Sleep, System.Threading.Timer lub alikes)

  2. Czekamy, ponieważ niektóre warunki zmieniają się w pewnym czasie ... keyword(s) is/are some time! jeśli warunek-check znajduje się w naszej domenie kodu, my powinien używać WaitHandles - w przeciwnym razie zewnętrzny komponent powinien podaj jakieś haczyki ... jeśli nie, jego konstrukcja jest zła!

Moja odpowiedź dotyczy głównie przypadków użycia 2

 141
Author: Andreas Niedermair,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-26 18:40:13

Scenariusz 1-oczekiwanie na zakończenie zadania asynchronicznego: zgadzam się, że WaitHandle/Auto|ManualResetEvent powinien być używany w scenariuszu, w którym wątek czeka na zakończenie zadania w innym wątku.

Scenariusz 2 - pętla czasowa while: jednak jako mechanizm czasowy (while+wątek.Sleep) jest idealnie w porządku dla 99% aplikacji, które nie wymagają wiedzy dokładnie kiedy zablokowany wątek powinien " obudzić się*. Argumentem, że do utworzenia wątku potrzeba 200k cykli jest również nieprawidłowe-wątek pętli czasowej i tak musi zostać utworzony, a 200k cykli to tylko kolejna duża liczba (powiedz mi, ile cykli otworzyć plik/gniazdo/db?).

Więc jeśli while + wątek.Sen działa, po co komplikować rzeczy? Tylko prawnicy składni byliby praktyczni !

 28
Author: Swab.Jat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-18 16:46:46

Chciałbym odpowiedzieć na to pytanie z perspektywy kodowania-polityki, która może, ale nie musi być dla nikogo pomocna. Ale szczególnie, gdy masz do czynienia z narzędziami, które są przeznaczone dla 9-5 programistów korporacyjnych, ludzie, którzy piszą dokumentację mają tendencję do używania słów takich jak " nie powinno "i " nigdy", aby oznaczać "nie rób tego, chyba że naprawdę wiesz, co robisz i dlaczego".

Kilka moich innych ulubionych w świecie C# to to, że mówią ci " nigdy nie dzwoń do Locka (tego)" lub " nigdy nie dzwoń GC.Collect ()". Te dwa są zdecydowanie deklarowane w wielu blogach i oficjalnej dokumentacji, a IMO są kompletną dezinformacją. Na pewnym poziomie ta dezinformacja służy swojemu celowi, ponieważ utrzymuje początkujących z dala od robienia rzeczy, których nie rozumieją, zanim w pełni zbadają alternatywy, ale jednocześnie utrudnia znalezienie prawdziwych informacji za pośrednictwem wyszukiwarek, które wydają się wskazywać na artykuły mówiące, abyś czegoś nie robił, nie oferując odpowiedzi na pytanie "dlaczego nie?"

Politycznie, to sprowadza się do tego, co ludzie uważają za "dobry projekt" lub "zły projekt". Oficjalna dokumentacja nie powinna dyktować projektu mojej aplikacji. Jeśli naprawdę istnieje jakiś techniczny powód, dla którego nie powinieneś wywoływać funkcji sleep(), to IMO dokumentacja powinna stwierdzać, że jest całkowicie w porządku wywoływać ją w określonych scenariuszach, ale może zaoferować alternatywne rozwiązania, które są niezależne od scenariuszy lub bardziej odpowiednie dla innych scenariuszy.

"sleep ()" jest przydatny w wielu sytuacjach, gdy terminy są jasno zdefiniowane w czasie rzeczywistym, jednak istnieją bardziej wyrafinowane systemy czekania i sygnalizowania wątków, które należy rozważyć i zrozumieć, zanim zaczniesz dodawać sleep () do kodu, a rzucanie niepotrzebnych instrukcji sleep () w kodzie jest ogólnie uważane za taktykę dla początkujących.

 6
Author: Jason Nelson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-11 18:47:41

Jest to 1).spinning i 2).pętla ankietowa z Twoimi przykładami, które ludzie ostrzegają przed , a nie przed wątkiem.Sen () część. Myślę, że nitka.Funkcja Sleep() jest zwykle dodawana w celu łatwego ulepszenia kodu, który jest wirujący lub w pętli ankietowej, więc jest po prostu kojarzona z "złym" kodem.

W dodatku ludzie robią takie rzeczy jak:

while(inWait)Thread.Sleep(5000); 

Gdzie zmienna inWait nie jest dostępna w sposób bezpieczny dla wątku, co również powoduje problemy.

Programiści chcą zobaczyć wątki sterowane przez zdarzenia i konstrukcje sygnalizacyjne i blokujące, a kiedy to zrobisz, nie będziesz potrzebować wątku.Sleep (), a także wyeliminowane zostały obawy dotyczące bezpiecznego dostępu do zmiennej thread. Jako przykład możesz utworzyć obsługę zdarzeń powiązaną z klasą FileSystemWatcher i użyć zdarzenia do uruchomienia drugiego przykładu zamiast zapętlania?

Jak wspomniał Andreas N., read Threading in C#, by Joe Albahari , jest naprawdę bardzo dobry.

 3
Author: mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-20 19:59:22

Sleep jest używany w przypadkach, gdy niezależne programy, nad którymi nie masz kontroli, mogą czasami używać powszechnie używanego zasobu (powiedzmy pliku), do którego twój program musi uzyskać dostęp, gdy jest uruchomiony, a gdy zasób jest używany przez te inne programy, Twój program jest zablokowany. W takim przypadku, gdy uzyskujesz dostęp do zasobu w kodzie, umieszczasz swój dostęp do zasobu w try-catch (aby złapać wyjątek, gdy nie możesz uzyskać dostępu do zasobu), i umieszczasz go w pętli while. Jeśli zasób jest wolny, sen nigdy nie zostanie wywołany. Ale jeśli zasób jest zablokowany, zasypiasz przez odpowiedni czas i próbujesz ponownie uzyskać dostęp do zasobu(dlatego zapętlasz). Pamiętaj jednak, że musisz umieścić jakiś ogranicznik na pętli, więc nie jest to potencjalnie nieskończona pętla. Możesz ustawić swój warunek ograniczający na N liczbę prób (to jest to, czego zwykle używam), lub sprawdzić zegar systemowy, dodać określoną ilość czasu, aby uzyskać limit czasu i zamknąć próba dostępu, jeśli przekroczysz limit czasu.

 3
Author: Steve Greene,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-22 15:05:29

Zgadzam się z wieloma tutaj, ale też myślę, że to zależy.

Ostatnio zrobiłem ten kod:

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();
}

To była prosta funkcja animate i użyłem na niej Thread.Sleep.

Mój wniosek, jeśli to zadziała, użyj go.
 -5
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-01 14:53:44

Dla tych z Was, którzy nie widzieli żadnego ważnego argumentu przeciwko używaniu wątku.Sen w scenariuszu 2, naprawdę jest jeden-wyjście aplikacji Jest wstrzymywane przez pętlę while (Scenariusz 1/3 jest po prostu głupi, więc nie zasługuje na więcej wzmianek)

Wielu, którzy udają, że są znanymi, krzyczącymi nićmi.Sleep is evil nie podał jednego ważnego powodu dla tych z nas, którzy domagali się praktycznego powodu, aby go nie używać - ale oto on, dzięki Pete ' owi - Thread.Sen jest zły (może być łatwo uniknąć za pomocą timera / Handlera)
    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;
    }
 -7
Author: Swab.Jat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 01:40:45