GMThreads
Co to jest ?
GMThreads to biblioteka DLL dzięki której można wykonywać podany kod GML w… wątkach ;D Właściwie, to to jest eksperyment i trzeba to przetestować.
Co to są “wątki” ? ;o
Hmm… nie chce mi się tego opisywać więc zacytuje:
Wątek (ang. thread) – to jednostka wykonawcza w obrębie jednego procesu, będąca kolejnym ciągiem instrukcji wykonywanym w obrębie tych samych danych (w tej samej przestrzeni adresowej).
Czytaj dalej…
Zdaje sobie z tego sprawę że większość użytkowników Game Makera nie wie o co biega więc łopatologicznie:
Wątek to taki “proces” który działa w tle aplikacji i wykonuje kod niezależnie od tego, co się z nią dzieje.
Do czego te wątki mogą mi się przydać ?
Cóż, kod wykonywany w wątkach jest cały czas tzn. nawet gdy okno gry jest zminimalizowane, przemieszczane, wyświetla wiadomość [show_message] itd. Poza tym, dzięki nim można ładować zasoby, wykonywać obliczenia i tak dalej w tle, przez co główne okno się nie “zawiesi” w czasie ładowania (FPS-y mogą spaść, chociaż to też zależy od priorytetu wątku).
Hmm… więc jak tego używać ?
Może najpierw wymienię funkcje:
thread_init( NazwaPliku ) – Inicjuje GMThreads. Można podać inną ścieżkę do pliku (domyślnie: GMThreads.dll). Ta funkcja musi być wykonana żeby korzystać z dll-a.
thread_create( SkryptGML, Wstrzymany? ) – Tworzy nowy wątek, w którym będzie wykonywany podany kod GML. Wątek można stworzyć wstrzymany. Zwraca uchwyt wątku, dzięki któremu można nim zarządzać za pomocą poniższych funkcji (jeśli wystąpił błąd w utworzeniu wątku – zwraca 0).
thread_suspend( UchwytWątku ) – Wstrzymuje podany wątek. Zwraca 1 jeśli nie wystąpił żaden błąd.
thread_resume( UchwytWątku ) – Wznawia podany wątek. Zwraca 1 jeśli nie wystąpił żaden błąd.
thread_priority( UchwytWątku, Priorytet ) – Ustawia priorytet podanego wątku. Zwraca 1 jeśli nie wystąpił żaden błąd.
Argument “Priorytet” można ustawić na:
0 = Bezczynny (Idle) – wątek będzie wykonywany gdy inne aplikacje nie będą używały procesora.
1 = Niski (Lowest)
2 = Poniżej normalnego (Low)
3 = Normalny (Normal) – Domyślny priorytet każdego wątku
4 = Powyżej normalnego (Higher)
5 = Wysoki (High)
6 = Czasu rzeczywistego (Time critical / Realtime) – Najwyższy priorytet, jako że wątek będzie korzystał z całej mocy procesora – wszystko w koło się zawiesi (system, myszka itd.), ale wątek będzie wykonywany.
thread__get_priority( UchwytWątku )- sprawdza jaki priorytet ma podany wątek, gdy wystąpi błąd przy sprawdzaniu zwraca -1.
thread_last_error() – zwraca ostatni kod błędu, ustawiony podczas zakończenia wątku ( 0 – brak błędu, 1 – błąd składni, 2 – Inny błąd w GML [nieznana zmienna itd.], 3 – wystąpił wyjątek w wątku ).
thread_terminate( UchwytWątku ) – Wymusza zamknięcie wątku. Zwraca 1 jeśli wątek został zamknięty. Ostrzeżenie: używać tylko w ostateczności – zamknięty wątek nie będzie mógł zwolnić użytych zasobów, doprowadzając do wycieków pamięci.
thread_is_suspended( UchwytWątku ) – Sprawdza czy podany wątek jest wstrzymany. Jeśli tak – zwraca 1 (true).
thread_is_running( UchwytWątku ) – Sprawdza czy podany wątek jest uruchomiony / czy istnieje. Jeśli tak – zwraca 1 (true).
thread_affinity_mask( UchwytWatku, Maska ) – ustawia maskę affinity (typ wektora bitowego) podanego wątku. Jak to działa ?
Każdy bit podanej wartości (maski) rozprezentuje procesor (rdzeń) czyli np.
3 w postaci binarnej to 0000 0011, więc wątek z taką maską będzie korzystał z pierwszego i drugiego rdzenia
10 w postaci binarnej to 0000 1010, więc wątek będzie korzystał z 2 i 4 rdzenia.
Żeby sobie dopasować taką wartość możecie użyć kalkulatora windowsowego w trybie naukowym.
Jeśli funkcja zawiedzie – zwraca 0, w innym wypadku zwraca poprzednia maskę.
more info: http://msdn.microsoft.com/en-us/library/ms686247(VS.85).aspx
thread_ideal_processor( UchwytWatku, NumerProcesora ) – ustawia preferowany procesor (rdzeń) dla wątku. Numer procesora ustawiany jest od 0, czyli 0 = pierwszy rdzeń, 1 = drugi itd.
Zwraca -1 gdy wystąpi błąd, inaczej zwróci poprzedni ustawiony numer procesora.
more info: http://msdn.microsoft.com/en-us/library/ms686253(VS.85).aspx
thread_num_of_processors() – Zwraca ilość rdzeni/procesorów.
Tworzenie wątku:
Wątek tworzy się za pomocą funkcji thread_create, której zwróconą wartość należałoby zapisać w zmiennej, żeby móc go później kontrolować np.
1 2 3 4 5 6 7 | moj_watek = thread_create( "repeat (1000) global.zmienna += 1;", 1 ); // stworz watek (wstrzymany)</p> if ( !moj_watek ) { thread_priority( moj_watek, 2 ); // ustaw priorytet na "ponizej normalnego" thread_resume( moj_watek ); // wznow watek } else show_message( "Nie można utworzyć wątku." ); |
Powyższy kod uruchomi wątek z niższym priorytetem, a jeśli nie uda się go stworzyć zostanie wyświetlona wiadomość z błędem. ;p Proste, nie ? Z takim kodem GML na pewno ;d
W podanym skrypcie GML można definiować zmienne ( zmienna = 0… ), ale nie będą one dostępne z żadnego obiektu ( nawet z tego w którym wątek został utworzony ), nie licząc zmiennych globalnych. Po wykonaniu kodu wątek zostaje zakończony.
“Nieskończone” wykonywanie kodu w wątkach
Też proste i chyba oczywiste. ;D Do tego można użyć pętli while. np.
1 2 3 4 5 6 7 8 9 | moj_watek = thread_create( " while ( 1 ) { if ( costam ) { zrob_cos(); zrob_cos(); } zmienna += 0.1; } ", 0 ); |
Jednak trzeba pamiętać, że kod w pętlach jest wykonywany niezależnie od prędkości pokoju (room_speed) i dlatego taki kod spowoduje większe użycie procesora. Tak, więc nieraz trzeba użyć sleep(), żeby opóźnić wykonywanie pętli np.
1 2 3 4 5 6 7 8 9 10 | moj_watek = thread_create( " while ( 1 ) { if ( costam ) { zrob_cos(); zrob_cos(); } zmienna += 0.1; sleep( 1000 ); // poczekaj sekunde } ", 0 ); |
Zakończenie wątku
Zakończyć wątek można za pomocą funkcji thread_terminate, ale używać jej należy tylko w ostateczności, gdyż wymuszenie zamknięcia wątku uniemożliwi mu zwolnienia zasobów z pamięci powodując tzw. memory leak. Żeby bezpiecznie zakończyć wątek trzeba skorzystać z warunków ;d przykładowy kod:
1 2 3 4 5 6 7 8 9 10 | moj_watek = thread_create( " while ( !global.zakonczony ) { // wykonuj dopoki watek nie zostal zakonczony usun_cos(); zrob_cos(); zrob_cos(); if ( global.zakonczony ) exit; // przerwij w tym miejscu jesli zostal zakonczony dodaj_cos(); zrob_cos(); } ", 0 ); |
i po prostu ustawiasz zmienną global.zakonczony na true ;o
Dodatkowe informacje:
Wątek jest wykonywany nawet podczas i po zmianie pokoju, dlatego trzeba sprawdzać czy dany obiekt czy zmienna istnieje. Gdy wystąpi błąd w GML (syntax error, nieznana zmienna czy obiekt itd.) wtedy wątek zostaje zakończony, bez wyświetlenia komunikatu błędu. No i można używać komentarzy ;D
Znane bugi:
- W wątkach GM-owych okienek wiadomości (show_message*) nie można zamknąć.
- W wątkach wszystkie otwierane okna nie blokują głównego okna gry.
- Jeśli wątek jest uruchomiony, wystąpi błąd w GML i user naciśnie “Abort” – wystąpi wyjątek AV.
|
|
download: GMThreads (1.4) (42.22KB) added: 02/08/2008 clicks: 165 description: |
W archiwum jest prosty przykład i DLL. Jeśli chcesz używać tej biblioteki – umieść mnie (Snake) w credits ;D
A tu test ładowania zasobów w wątku: http://www.gmclan.org/up541_4_ThreadLoadTest.html – powinno załadować 2MB obraz JPG bez zamrożenia okna.
DLL działa tylko z GM6.1 i GM7.0


