Skocz do zawartości

[DLL] GMThreads


Rekomendowane odpowiedzi

gmthreads.png


Last update: 29.12.2009:
Features:
- Wsparcie dla GM8
- Dodano funkcję thread_wait, służącą do oczekiwania na zakończenie wątku
- Dodano możliwość sprawdzenia błędu danego wątku
- DLL został napisany od nowa w MASMie
- Wydano kod źródłowy (na LGPL)

Compatibility issues:
- Zmiany w nazwach funkcji (thread_priority, thread_affinity_mask, thread_ideal_processor, thread_num_of_processors)
- Usunięto funkcje thread_last_error i thread_is_suspended
- Teraz po utworzeniu wątku trzeba zamykać jego uchwyt (za pomocą thread_close). Dzięki temu można przechwycić kod błędu wątków (thread_get_error).


FAQ style

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).


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). Uchwyt powinien zostać zamknięty za pomocą funkcji thread_close.


thread_close( UchwytWątku )
- Zamyka uchwyt podanego wątku. Uwaga: Wątek nie jest zakańczany po wywołaniu tej funkcji.


thread_wait( UchwytWątku, Czas )
- Wstrzymuje wątek, który wywołuje tą funkcję do czasu aż podany wątek nie zakończy pracy lub dopóki nie upłynie podana ilość czasu. Czas podajemy w milisekundach, podając -1 funkcja wróci jedynie, gdy podany wątek zakończy pracę. Zwraca -1 jeśli wystąpił błąd (np. podano nieprawidłowy uchwyt), 0 gdy upłynęła podana ilość czasu a wątek nadal nie zakończył pracy i 1 gdy wątek zakończył pracę.


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_set_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_get_error( UchwytWątku )
- zwraca kod błędu, który wystąpił w danym wątku. ( -1 - zły uchwyt, 0 - brak błędu, 1 - błąd GML ).

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_running( UchwytWątku )
- Sprawdza czy podany wątek jest uruchomiony / czy istnieje. Jeśli tak - zwraca 1 (true).


thread_set_affinity( UchwytWatku, Maska )
- ustawia maskę affinity (typ wektora bitowego) podanego wątku. Jak to działa ?

Każdy bit podanej wartości (maski) reprezentuje 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:


thread_set_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:


thread_get_cpucount()
- 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.

GML
moj_watek = thread_create(
"repeat (1000) global.zmienna += 1;"
, 1 );
// stworz watek (wstrzymany)

if
( moj_watek ) {

thread_set_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.

GML
moj_watek = thread_create(
"

while ( true ) {

if ( costam ) {

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.

GML
moj_watek = thread_create(
"

while (1) {

if ( costam ) {

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:

GML
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.

Download (V2.0):
http://www.gmclan.org/up541_4_GMThreads2.html (~9KB)
W archiwum jest prosty przykład, DLL i kod źródłowy. Jeśli chcesz używać tej biblioteki - umieść mnie w credits ;D

Podziękowania dla Maxpayna, za testowanie nowej wersji na swoim procku (Quad) :D

Jeśli masz konto na GMC, możesz napisać w tym temacie swoją ocenę: ;D
http://gmc.yoyogames.com/index.php?showtopic=390517

Testujcie i zgłaszajcie bugi ;d

DLL działa tylko z GM6.1, GM7.0 i GM8.0
Odnośnik do komentarza
Udostępnij na innych stronach

  • Odpowiedzi 172
  • Dodano
  • Ostatniej odpowiedzi

Top użytkownicy w tym temacie

Top użytkownicy w tym temacie

btw co to ma być. 2 min, 213 sek ?

lul xd zapomniałem modulo pewnie ;D

 

rozumiem, że taki wątek wykonuje sprawniej(szybciej) kod niż gm =P

Nie szybciej tylko asynchronicznie z grą ;p nie może być szybciej bo okno GM-a rysuje obraz w trakcie wykonywania kodu w wątkach ;p

No chyba że ustawisz priorytet na time critical ;D

Odnośnik do komentarza
Udostępnij na innych stronach

Jak zamrozisz/wyłączysz rysowanie obrazu w GM to tak. Ale chodzi tu o to hmm... np. używasz supersound.dll (taki DLL dla GM który odtwarza dźwięk) i chcesz załadować jakiś plik z muzyką, to okno gry się zamrozi dopóki muzyka się nie załaduje. A jeśli załadujesz muzykę przez wątek to zamrozi się tylko wątek dopóki się nie załaduje, a gra będzie normalnie chodzić ;p

 

Szybciej niż normalnie w GM może działać jak zmienisz priorytet wątku na "Czasu rzeczywistego", bo użyje wtedy całego procesora.

Odnośnik do komentarza
Udostępnij na innych stronach

Wow! Jeśli to faktycznie będzie działać bez problemu, to niemal na pewno zastosuje Twój .dll w ArcMagi :D. Właśnie tego mi w GM brakowało - możliwości ładowania zasobów w odzielnym wątku (lub przynajmniej streamowania muzyki).

Mógłbym zaoszczędzić sporo pamięci wgrywając zasoby tylko kiedy są faktycznie potrzebne, jednocześnie nie powodując drobnych "przeskoków" w czasie gry.

 

Świetna robota, Snake!

Odnośnik do komentarza
Udostępnij na innych stronach

Trzeba sprawdzić dokładniej z tym ładowaniem, bo np. jak użyłem sprite_add() z zaznaczoną opcją "Preload", to samo załadowanie do pamięci pliku nie zawiesiło gry, ale ten "preload" jest wykonywany poza wątkami i dlatego przetwarzanie załadowanego sprite'a już przywiesiło ;[ (a samo ładowanie w sprite_add bez opcji preload nie zawiesza okna)

Odnośnik do komentarza
Udostępnij na innych stronach

Auć :(. Szkoda, miałem nadzieję, że ładowanie do pamięci graficznej z preloada też poleci od razu w dodatkowym wątku. Bez tego faktycznie będzie ścinać niemal tak samo jak bez zabawy w multithreading. Przynajmniej muzykę będę mógł ładować poza grą, to już zawsze coś.

 

Ech... nie ma lekko.

Odnośnik do komentarza
Udostępnij na innych stronach

Snake, jakbyś zrobił odzielną funkcję, która by ładowała sprite do zasobów i od razu do pamięci graficznej, to to by było cholernie dobre. Kurde, nowa jakość resource managmentu w GM :). Moim zdaniem, jeśli masz czas i ochotę, to naprawdę warto.

Odnośnik do komentarza
Udostępnij na innych stronach

Haha, mam dobrą wiadomość :D

Nie wiem czemu mi wcześniej zamrażało okno, ale... właśnie chciałem spróbować zrobić tego hacka na grafikę, dla testu dopisałem sprite/background add/replace w wątku i... załadowało się bez żadnej przycinki xD

 

Ale przetestujcie ten przykład (gm6 i gmk) dla pewności:

http://www.gmclan.org/up541_4_ThreadLoadTest.html

(ładuje 2MB plik JPG w wątku)

Odnośnik do komentarza
Udostępnij na innych stronach

u mnie przycina, gdzies na 0.5 sekundy, dziwne o.o

Odnośnik do komentarza
Udostępnij na innych stronach

PsichiX, a spróbuj się przyjrzeć, czy nie rysuje przez to pół sekundy obrazu, czy może blokuje całego GM-a (kod gml też się nie wykonuje)

 

Kurde, u mnie i u Pietera działa ;/

Aha, i jak możesz to porównaj ładowanie bez wątku tego pliku z ładowaniem w wątku ;p

Odnośnik do komentarza
Udostępnij na innych stronach

w sumie to chyba tylko wina rysowania, no ale fpsy ciagle na maxa po zmianie mialem. mnie to tylko interesuje czy Ty ten kod gmla assemblerem wstrzykujesz do watku czy jak, bo nie moge zrozumiec jakim cudem to dziala - sam sie swojego czasu z tym bawilem. PS. fakt, do YYG nie startuj bo pociagna Cie po sadach - idioci zamiast doceniac wklad innych i pomoc w ulepszanie oraz poszerzanie mozliwosci gma, sami sobie utrudniaja zycie - chociaz w koncu kto by chcial by ingerowano w ich dzielo.

Odnośnik do komentarza
Udostępnij na innych stronach

PsihiX, to nie jest jedyny przypadek gdy firma ciągla ludzi po sądach za to że usprawniają prace ich dzieła, np. był przypadek z Creative jak ktoś napisał sterownik, to na oficjalnym forum odrazu usuneli temat, a autor miał problemy z prawem. Szara rzeczywistość.

 

Co do pomysłu, ######ście ogólnie, nigdy nie wiedziałem o co chodzi tak dokładnie w wątkach, ale już kapuje. ;p

 

BTW Snake wejdź na gadu?

Odnośnik do komentarza
Udostępnij na innych stronach

mnie to tylko interesuje czy Ty ten kod gmla assemblerem wstrzykujesz do watku czy jak, bo nie moge zrozumiec jakim cudem to dziala

Nieważne jak to zrobiłem - ważne że działa ;D

 

Edit: Przykład sprawdzałem, ładuje się bez zająknięcia.
w sumie to chyba tylko wina rysowania, no ale fpsy ciagle na maxa po zmianie mialem.

To świetnie B) więc dzięki mojej bibliotece można znacznie lepiej zarządzać zasobami i nie tylko, w GM :)

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.

×
×
  • Dodaj nową pozycję...