Dwie uwagi o śpiących wątkach

2009-11-29 14:46

Wszyscy znamy doskonale funkcję Sleep, która w Windows API służy do zawieszania działania wątku na określony czas (podawany w milisekundach). Wydawałoby się, że musi to być najprostsza funkcja z tego API, jaką tylko można sobie wyobrazić – bo co może być skomplikowanego w “zwykłej pauzie”? A okazuje się, że jak najbardziej może :)

Używając Sleep – zwłaszcza w swej zwykłej wersji – musimy bowiem pamiętać przynajmniej o dwóch sprawach:

  1. Wątek, w którym wywołamy tę funkcję, zostanie zawieszony całkowicie aż do momentu, gdy podany interwał czasowy się skończy . W szczególności Sleep(INFINITE); sprawi, że właściwie możemy ów wątek wyrzucić do kosza, gdyż nie da się już go odwiesić (funkcja ResumeThread wywołana z innego wątku nic tu nie pomoże).
    Również przyjście komunikatu okna w czasie, gdy nasz wątek smacznie śpi, nic nie zmienia. Wiadomości takie będą nieobsłużone aż do odwieszenia się wątku. Wynika stąd fakt, że uśpienie na dłuższy czas wątku, w którym obsługujemy UI, będzie dla użytkownika natychmiast zauważalne jako zawieszenie się programu. Jeśli chcemy, by w czasie przerwy aplikacja odpowiadała na komunikaty, należy zastosować inną funkcję (np. MsgWaitForMultipleObjectsEx albo po prostu GetTickCount wraz z wewnętrzną pętlą komunikatów).
    Istnieje też wariant Ex funkcji Sleep. Różni się on od oryginału tym, że rozpoczęte przez niego oczekiwanie można przerwać, jeśli sobie tego zażyczymy. Wątek uśpiony przez SleepEx może być przedwcześnie obudzony, gdy otrzyma informacje o zakończeniu asynchronicznej operacji I/O lub asynchronicznego wywołania procedury (APC).
  2. Czas, na jaki faktycznie uśpimy nasz wątek, będzie prawie na pewno dłuższy niż ten, który podamy jako parametr funkcji Sleep. Jest on bowiem determinowany przez długość tzw. kwantów czasu (time slices), jakie system operacyjny przydziela kolejnym wątkom, by zapewnić złudzenie ich jednoczesnego wykonania. Działanie Sleep polega w rzeczywistości na oddaniu systemowi reszty kwantu czasu, który został przydzielony wątkowi; przestawienie wątku w stan “nieuruchamialności” na podaną ilość milisekund; a następnie na wznowieniu jego pracy, gdy ponownie otrzyma czas procesora od systemowego schedulera. W sumie więc czas uśpienia będzie równy:

    PozostałyKwantCzasu + ParametrSleep + CzasDoUaktywnieniaWątku

    Stąd wynikają dwa wnioski. Po pierwsze, nie powinniśmy nigdy używać Sleep jako sposobu na mierzenie czasu – już poczciwy GetTickCount sprawi się tu znacznie lepiej. Po drugie, wywołanie Sleep(0); jest jak najbardziej dopuszczalne i oznacza przedwczesne zrzeczenie się kwantu czasu, jaki wątek dostał od systemu. W czasach 16-bitowych wersji Windows i wielowątkowości bez wywłaszczała była od tego specjalna funkcja Yield, którą należało często wywoływać, aby przełączanie wątków w ogóle było możliwe. Teraz rzecz jasna nie jest to konieczne, ale nadal może być przydatne dla zasygnalizowania, że nasz wątek nie robi nic pożytecznego, a tylko w brzydki sposób na coś czeka (tzw. busy waiting).

O tych dwóch szczegółach odnośnie funkcji Sleep dobrze jest pamiętać, jeśli nasze wątki chcemy usypiać. Jako programiści Windows możemy się aczkolwiek podbudować tym, że nie mamy przy tym takich problemów jak koderzy piszący pod Linuksem. Tam sleep może być potencjalnie zaimplementowany na sygnałach, co wymaga ostrożności przy stosowaniu go razem z funkcjami alarm i signal.

Tags: ,
Author: Xion, posted under Programming »



Adding comments is disabled.

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.