====== Différences ====== Ci-dessous, les différences entre deux révisions de la page.
allegro:threads [2012/03/16 23:49] mrhide [Basic Example] |
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 17: | Ligne 15: | ||
typedef struct s_data | typedef struct s_data | ||
{ | { | ||
- | ALLEGRO_MUTEX *mutex; | + | ALLEGRO_MUTEX *mutex; |
- | ALLEGRO_COND *cond; | + | ALLEGRO_DISPLAY *display; |
- | ALLEGRO_DISPLAY *display; | + | ALLEGRO_BITMAP *caret; |
- | ALLEGRO_BITMAP *caret; | + | |
} DATA; | } DATA; | ||
void init_DATA(DATA *data, ALLEGRO_DISPLAY *display, ALLEGRO_BITMAP *caret) | void init_DATA(DATA *data, ALLEGRO_DISPLAY *display, ALLEGRO_BITMAP *caret) | ||
{ | { | ||
- | 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; | + | |
} | } | ||
void dest_DATA(DATA *data) | void dest_DATA(DATA *data) | ||
{ | { | ||
- | al_destroy_mutex(data->mutex); | + | al_destroy_mutex(data->mutex); |
- | al_destroy_cond(data->cond); | + | |
} | } | ||
Ligne 48: | Ligne 43: | ||
int main(int argc, char **argv) | int main(int argc, char **argv) | ||
{ | { | ||
- | ALLEGRO_DISPLAY *display = NULL; | + | ALLEGRO_DISPLAY *display = NULL; |
- | ALLEGRO_EVENT_QUEUE *event_queue = NULL; | + | ALLEGRO_EVENT_QUEUE *event_queue = NULL; |
- | ALLEGRO_BITMAP *caret = NULL; | + | ALLEGRO_BITMAP *caret = NULL; |
- | ALLEGRO_THREAD *thread = NULL; | + | ALLEGRO_THREAD *thread = NULL; |
- | DATA data; | + | DATA data; |
+ | |||
+ | if(!al_init()) { | ||
+ | fprintf(stderr, "failed to initialize allegro!\n"); | ||
+ | return -1; | ||
+ | } | ||
- | if(!al_init()) { | + | al_set_new_display_flags(ALLEGRO_NOFRAME | ALLEGRO_WINDOWED); |
- | fprintf(stderr, "failed to initialize allegro!\n"); | + | display = al_create_display(SCREEN_W, SCREEN_H); |
- | return -1; | + | if(!display) { |
- | } | + | fprintf(stderr, "failed to create display!\n"); |
+ | return -1; | ||
+ | } | ||
- | al_set_new_display_flags(ALLEGRO_NOFRAME | ALLEGRO_WINDOWED); | + | caret = al_create_bitmap(CARET_WIDTH, CARET_HEIGHT); |
- | display = al_create_display(SCREEN_W, SCREEN_H); | + | if(!caret) { |
- | if(!display) { | + | fprintf(stderr, "failed to create bouncer bitmap!\n"); |
- | fprintf(stderr, "failed to create display!\n"); | + | al_destroy_display(display); |
- | return -1; | + | return -1; |
- | } | + | } |
- | caret = al_create_bitmap(CARET_WIDTH, CARET_HEIGHT); | + | al_set_target_bitmap(caret); |
- | if(!caret) { | + | al_clear_to_color(al_map_rgb(255, 0, 255)); |
- | fprintf(stderr, "failed to create bouncer bitmap!\n"); | + | al_set_target_bitmap(al_get_backbuffer(display)); |
- | al_destroy_display(display); | + | event_queue = al_create_event_queue(); |
- | return -1; | + | |
- | } | + | |
- | al_set_target_bitmap(caret); | + | if(!event_queue) { |
- | al_clear_to_color(al_map_rgb(255, 0, 255)); | + | fprintf(stderr, "failed to create event_queue!\n"); |
- | al_set_target_bitmap(al_get_backbuffer(display)); | + | al_destroy_bitmap(caret); |
- | event_queue = al_create_event_queue(); | + | al_destroy_display(display); |
- | + | return -1; | |
- | if(!event_queue) { | + | } |
- | fprintf(stderr, "failed to create event_queue!\n"); | + | |
- | al_destroy_bitmap(caret); | + | |
- | al_destroy_display(display); | + | |
- | return -1; | + | |
- | } | + | |
| | ||
- | al_register_event_source(event_queue, al_get_display_event_source(display)); | + | al_register_event_source(event_queue, al_get_display_event_source(display)); |
- | al_clear_to_color(al_map_rgb(0,0,0)); | + | al_clear_to_color(al_map_rgb(0,0,0)); |
- | al_flip_display(); | + | al_flip_display(); |
- | init_DATA(&data, display, caret); | + | init_DATA(&data, display, caret); |
- | thread = al_create_thread(progressBar, &data); | + | thread = al_create_thread(progressBar, &data); |
- | al_start_thread(thread); | + | al_start_thread(thread); |
| | ||
- | al_rest(10.0); /* Loading some awesome datas !! */ | + | al_rest(10.0); /* Loading some awesome datas !! */ |
| | ||
- | //al_set_thread_should_stop(thread); /* appelée implicitement par l'instruction ci-dessous */ | + | //al_set_thread_should_stop(thread); /* appelée implicitement par l'instruction ci-dessous */ |
- | al_join_thread(thread, NULL); | + | al_join_thread(thread, NULL); |
| | ||
- | al_destroy_thread(thread); | + | al_destroy_thread(thread); |
- | dest_DATA(&data); | + | dest_DATA(&data); |
- | al_destroy_bitmap(caret); | + | al_destroy_bitmap(caret); |
- | al_destroy_display(display); | + | al_destroy_display(display); |
- | al_destroy_event_queue(event_queue); | + | al_destroy_event_queue(event_queue); |
- | return 0; | + | return 0; |
} | } | ||
- | static void *progressBar(ALLEGRO_THREAD *thr, void *arg) { | + | static void *progressBar(ALLEGRO_THREAD *thr, void *arg) |
+ | { | ||
+ | ALLEGRO_TIMER *timer = NULL; | ||
+ | ALLEGRO_EVENT_QUEUE *event_queue = NULL; | ||
+ | ALLEGRO_EVENT ev; | ||
- | ALLEGRO_TIMER *timer = NULL; | + | DATA *data = (DATA*) arg; |
- | ALLEGRO_EVENT_QUEUE *event_queue = NULL; | + | float num = 2.0; |
- | ALLEGRO_EVENT ev; | + | float xpos = PADDING; |
- | + | ||
- | DATA *data = (DATA*) arg; | + | |
- | float num = 2.0; | + | |
- | float xpos = PADDING; | + | |
- | + | ||
- | if (!data->display || !data->caret) { | + | |
- | fprintf(stderr, "args are NULL!\n"); | + | |
- | return NULL; | + | |
- | } | + | |
- | + | ||
- | timer = al_create_timer(1.0 / FPS); | + | |
- | if(!timer) { | + | |
- | fprintf(stderr, "failed to create timer!\n"); | + | |
- | return NULL; | + | |
- | } | + | |
- | + | ||
- | event_queue = al_create_event_queue(); | + | |
- | if(!event_queue) { | + | |
- | fprintf(stderr, "failed to create event queue!\n"); | + | |
- | return NULL; | + | |
- | } | + | |
- | al_register_event_source(event_queue, al_get_timer_event_source(timer)); | + | |
- | al_start_timer(timer); | + | |
- | al_set_target_bitmap(al_get_backbuffer(data->display)); /* new context */ | + | |
- | + | ||
- | while(!al_get_thread_should_stop(thr)) | + | |
- | { | + | |
- | al_wait_for_event(event_queue, &ev); | + | |
- | // if(ev.type == ALLEGRO_EVENT_TIMER) /* ne peut être autre chose */ | + | if (!data->display || !data->caret) { |
- | + | fprintf(stderr, "args are NULL!\n"); | |
- | al_draw_bitmap(data->caret, xpos, SCREEN_H-PADDING-CARET_HEIGHT, 0); | + | return NULL; |
- | + | } | |
- | xpos += num; | + | |
- | if (xpos >= SCREEN_W-PADDING) | + | timer = al_create_timer(1.0 / FPS); |
- | break; | + | if(!timer) { |
- | + | fprintf(stderr, "failed to create timer!\n"); | |
- | al_flip_display(); | + | return NULL; |
- | } | + | } |
- | + | ||
- | al_destroy_timer(timer); | + | event_queue = al_create_event_queue(); |
- | al_destroy_event_queue(event_queue); | + | if(!event_queue) { |
- | return NULL; | + | fprintf(stderr, "failed to create event queue!\n"); |
+ | return NULL; | ||
+ | } | ||
+ | al_register_event_source(event_queue, al_get_timer_event_source(timer)); | ||
+ | al_start_timer(timer); | ||
+ | al_set_target_bitmap(al_get_backbuffer(data->display)); /* new context */ | ||
+ | |||
+ | while(!al_get_thread_should_stop(thr)) | ||
+ | { | ||
+ | al_wait_for_event(event_queue, &ev); | ||
+ | // if(ev.type == ALLEGRO_EVENT_TIMER) /* ne peut être autre chose */ | ||
+ | |||
+ | al_draw_bitmap(data->caret, xpos, SCREEN_H-PADDING-CARET_HEIGHT, 0); | ||
+ | |||
+ | xpos += num; | ||
+ | if (xpos >= SCREEN_W-PADDING) | ||
+ | break; | ||
+ | |||
+ | al_flip_display(); | ||
+ | } | ||
+ | |||
+ | al_destroy_timer(timer); | ||
+ | al_destroy_event_queue(event_queue); | ||
+ | return NULL; | ||
} | } | ||
</file> | </file> | ||
- | [[allegro:input|Précédent]] << [[allegro:start|Sommaire]] >> [[allegro:addons|Suivant]] | + | ==== 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#articles|Sommaire]] >> [[allegro:addons|Suivant]] |