====== Différences ====== Ci-dessous, les différences entre deux révisions de la page.
allegro:threads [2012/03/16 23:54] mrhide [Exemple basique] |
allegro:threads [2012/06/29 17:02] (Version actuelle) mrhide [Threads] |
||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | ===== Threads ===== | + | ===== Allegro — Threads ===== |
Dans cet article nous allons voir comment utiliser l'interface de threading d'Allegro. | Dans cet article nous allons voir comment utiliser l'interface de threading d'Allegro. | ||
- | |||
- | FIXME faire le walktrough | ||
==== Exemple basique ==== | ==== Exemple basique ==== | ||
Ligne 18: | Ligne 16: | ||
{ | { | ||
ALLEGRO_MUTEX *mutex; | ALLEGRO_MUTEX *mutex; | ||
- | ALLEGRO_COND *cond; | ||
ALLEGRO_DISPLAY *display; | ALLEGRO_DISPLAY *display; | ||
ALLEGRO_BITMAP *caret; | ALLEGRO_BITMAP *caret; | ||
Ligne 26: | Ligne 23: | ||
{ | { | ||
data->mutex = al_create_mutex(); | data->mutex = al_create_mutex(); | ||
- | data->cond = al_create_cond(); | ||
data->display = display; | data->display = display; | ||
data->caret = caret; | data->caret = caret; | ||
Ligne 34: | Ligne 30: | ||
{ | { | ||
al_destroy_mutex(data->mutex); | al_destroy_mutex(data->mutex); | ||
- | al_destroy_cond(data->cond); | ||
} | } | ||
Ligne 109: | Ligne 104: | ||
} | } | ||
- | static void *progressBar(ALLEGRO_THREAD *thr, void *arg) { | + | static void *progressBar(ALLEGRO_THREAD *thr, void *arg) |
+ | { | ||
ALLEGRO_TIMER *timer = NULL; | ALLEGRO_TIMER *timer = NULL; | ||
ALLEGRO_EVENT_QUEUE *event_queue = NULL; | ALLEGRO_EVENT_QUEUE *event_queue = NULL; | ||
Ligne 161: | Ligne 156: | ||
==== Détails ==== | ==== Détails ==== | ||
+ | <code c> | ||
+ | typedef struct s_data | ||
+ | { | ||
+ | ALLEGRO_MUTEX *mutex; | ||
+ | ALLEGRO_DISPLAY *display; | ||
+ | ALLEGRO_BITMAP *caret; | ||
+ | } DATA; | ||
+ | |||
+ | void init_DATA(DATA *data, ALLEGRO_DISPLAY *display, ALLEGRO_BITMAP *caret) | ||
+ | { | ||
+ | data->mutex = al_create_mutex(); | ||
+ | data->display = display; | ||
+ | data->caret = caret; | ||
+ | } | ||
+ | |||
+ | void dest_DATA(DATA *data) | ||
+ | { | ||
+ | al_destroy_mutex(data->mutex); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | On crée une structure qui contient les ressources partagées, elle doit contenir minimum le **mutex** qui permet de demander l'accès exclusif aux données afin d'éviter de les corrompre (si par exemple 2 threads veulent modifier la même variable au même moment, la valeur finale de la variable est inconnue). | ||
+ | |||
+ | //On peut ajouter une **condition** de type **ALLEGRO_COND** qui permet à un thread d'en interrompre un autre et le de relancer grâce aux fonctions **al_wait_cond** et **al_broadcast_cond**, utile pour les threads concurrents.// | ||
+ | |||
+ | <code c> | ||
+ | thread = al_create_thread(progressBar, &data); | ||
+ | al_start_thread(thread); | ||
+ | | ||
+ | al_rest(10.0); /* Loading some awesome datas !! */ | ||
+ | </code> | ||
+ | |||
+ | On crée notre thread avec **al_create_thread** qui prend en paramètres une fonction (qui sera appelée par le thread nouvellement crée) et un argument (qui sera transmis à la fonction). | ||
+ | |||
+ | **al_start_thread** démarre notre thread, ensuite on peut faire tourner le code gros consommateur de temps (représenté par **al_rest**), on sait que l'affichage est géré par la fonction **progressBar**. | ||
+ | |||
+ | C'est une fonction très simple que reprend des notions des articles précédents. Sachez que dessiner la barre de progression avec un bitmap c'est pas très intelligent ! on dispose de fonctions de dessin via ce greffon : [[allegro:addon_primitives|Primitive]]. | ||
+ | |||
+ | On remarquera seulement ceci : | ||
+ | |||
+ | <code c> | ||
+ | al_set_target_bitmap(al_get_backbuffer(data->display)); /* new context */ | ||
+ | </code> | ||
+ | |||
+ | En effet, chaque thread possède son propre context, c'est à dire tout ce qui est global dans allegro est limité à un thread, donc pour pouvoir dessiner sur le canvas démarré dans le thread principal, il faut le transmettre via notre structure et prévenir notre nouveau thread.\\ | ||
+ | Si votre jeu termine sur un **ASSERT**, c'est certainement du à une variable du context qui est à **NULL**, donc faites attention ! | ||
+ | |||
+ | ==== Amelioration ==== | ||
+ | |||
+ | On décide d'améliorer notre barre de progression, on veut que le thread principal contrôle l'avancement de la barre : on ajoute donc la variable **data.percent**, un entier compris entre 0 et 100 pour définir la taille de notre barre en fonction de l'avancement dans notre thread principal.\\ | ||
+ | On doit aussi ajouter une condition pour que le thread principal déclenche le dessin dès qu'il modifie la variable. | ||
+ | |||
+ | On modifie alors notre fonction qui dépend plus du timer : | ||
+ | |||
+ | <code c> | ||
+ | static void *progressBar(ALLEGRO_THREAD *thr, void *arg) | ||
+ | { | ||
+ | DATA *data = (DATA*) arg; | ||
+ | float pbar_1pc = (SCREEN_W-2*PADDING)/100.0; | ||
+ | int goon = 1; | ||
+ | |||
+ | al_set_target_bitmap(al_get_backbuffer(data->display)); | ||
+ | |||
+ | while(!al_get_thread_should_stop(thr) && goon) | ||
+ | { | ||
+ | al_lock_mutex(data->mutex); | ||
+ | al_wait_cond(data->cond, data->mutex); | ||
+ | al_draw_bitmap(data->caret, data->percent*pbar_1pc, SCREEN_H-PADDING-CARET_HEIGHT, 0); | ||
+ | if (data->percent == 100) | ||
+ | goon = 0; | ||
+ | al_unlock_mutex(data->mutex); | ||
+ | |||
+ | al_flip_display(); | ||
+ | } | ||
+ | |||
+ | return NULL; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | La fonction restera bloquée sur **al_wait_cond** tant que le thread parent n'a pas appelé **al_broadcast_cond**. | ||
+ | |||
+ | Pour terminer ce type de thread, un **join** ne suffit pas car il n'enlève pas les **locks** dûs aux conditions. Vous devez utiliser à la place : | ||
+ | |||
+ | <code C> | ||
+ | al_set_thread_should_stop(thread); | ||
+ | al_lock_mutex(data.mutex); | ||
+ | al_broadcast_cond(data.cond); | ||
+ | al_unlock_mutex(data.mutex); | ||
+ | </code> | ||
+ | Maintenant que vous savez utiliser les threads, vous savez pratiquement tout ce qu'il y a à savoir sur le core d'allegro, dans la suite de ce tutoriel nous aborderons les différents greffons d'Allegro. | ||
- | [[allegro:input|Précédent]] << [[allegro:start|Sommaire]] >> [[allegro:addons|Suivant]] | + | [[allegro:input|Précédent]] << [[allegro:start#articles|Sommaire]] >> [[allegro:addons|Suivant]] |