Outils pour utilisateurs

Outils du site


allegro:threads

====== Différences ====== Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

allegro:threads [2011/12/19 21:54]
mrhide [Walk through]
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.
  
-==== Basic Example ​====+==== Exemple basique ​====
  
-Danc cet exemple nous utiliserons le même bitmap que dans l'exemple précédentmais au lieun de changer ​les axes avec les sourisnous allons utiliser des threads pour le faire.\\ +Comme Allegro est basé sur un système d'events, les threads sont assez peu utiles : ils servent à répartir la charge si on a plusieurs coeursou à gérer ​le réseau si votre jeu est multijoueur.
-Définissons :+
  
-Thread Parent: Tout ce qui est dans notre fonction **int main()**. +Dans cet exemple, nous allons créer un thread ​qui va faire avancer une barre de progression pendant que le thread principal ​est occupé ​(à charger un niveau, des images, ​...).
- +
-Premier Thread: Le premier Thread crée. +
- +
-Second Thread: Le second thread crée.+
  
 <file c main.c> <file c main.c>
Ligne 18: Ligne 13:
 #include <​allegro5/​allegro.h>​ #include <​allegro5/​allegro.h>​
  
-class DATA{+typedef struct s_data  
 +{ 
 +   ​ALLEGRO_MUTEX ​  ​*mutex;​ 
 +   ​ALLEGRO_DISPLAY *display; 
 +   ​ALLEGRO_BITMAP ​ *caret; 
 +} DATA;
  
-   ​public:​+void init_DATA(DATA *data, ALLEGRO_DISPLAY *display, ALLEGRO_BITMAP ​ *caret) 
 +
 +   ​data->​mutex ​  = al_create_mutex();​ 
 +   ​data->​display = display; 
 +   ​data->​caret ​  = caret; 
 +}
  
-      ALLEGRO_MUTEX ​*mutex; +void dest_DATA(DATA ​*data
-      ALLEGRO_COND ​ *cond; +
-      float          posiX; +   ​al_destroy_mutex(data->mutex); 
-      float          posiY; +}
-      bool           ​modi_X;​ +
-      bool           ​ready;​ +
- +
-   DATA(: mutex(al_create_mutex()),​ +
-            ​cond(al_create_cond()),​ +
-            posiX (0), +
-            posiY (0), +
-            modi_X(false),​ +
-            ready (false) ​{+
- +
-   ~DATA(){ +
- +
-      ​al_destroy_mutex(mutex);​ +
-      ​al_destroy_cond(cond);​ +
- +
-   } +
- +
-};+
  
 const float FPS        = 30; const float FPS        = 30;
 const int SCREEN_W ​    = 640; const int SCREEN_W ​    = 640;
 const int SCREEN_H ​    = 480; const int SCREEN_H ​    = 480;
-const int BOUNCER_SIZE ​= 32;+const int CARET_HEIGHT ​= 32
 +const int CARET_WIDTH ​ = 16; 
 +const int PADDING ​     = 64;
  
-static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg);+static void *progressBar(ALLEGRO_THREAD *thr, void *arg);
  
 int main(int argc, char **argv) int main(int argc, char **argv)
Ligne 56: Ligne 45:
    ​ALLEGRO_DISPLAY ​    ​*display ​    = NULL;    ​ALLEGRO_DISPLAY ​    ​*display ​    = NULL;
    ​ALLEGRO_EVENT_QUEUE *event_queue = NULL;    ​ALLEGRO_EVENT_QUEUE *event_queue = NULL;
-   ALLEGRO_TIMER ​      ​*timer ​      = NULL; +   ​ALLEGRO_BITMAP ​     *caret       = NULL; 
-   ALLEGRO_BITMAP ​     *bouncer ​    = NULL; +   ​ALLEGRO_THREAD ​     *thread ​     ​= NULL; 
-   ​ALLEGRO_THREAD ​     *thread_1 ​   ​= NULL; +   DATA data
-   ALLEGRO_THREAD ​     *thread_2 ​   = NULL; +   
- +
-   bool redraw = true; +
    ​if(!al_init()) {    ​if(!al_init()) {
       fprintf(stderr,​ "​failed to initialize allegro!\n"​);​       fprintf(stderr,​ "​failed to initialize allegro!\n"​);​
Ligne 68: Ligne 54:
    }    }
  
-   if(!al_install_mouse()) { +   al_set_new_display_flags(ALLEGRO_NOFRAME | ALLEGRO_WINDOWED);
-      fprintf(stderr,​ "​failed to initialize the mouse!\n"​);​ +
-      return -1; +
-   } +
- +
-   timer = al_create_timer(1.0 / FPS); +
-   ​if(!timer) { +
-      fprintf(stderr,​ "​failed to create timer!\n"​);​ +
-      return -1; +
-   } +
    ​display = al_create_display(SCREEN_W,​ SCREEN_H);    ​display = al_create_display(SCREEN_W,​ SCREEN_H);
    ​if(!display) {    ​if(!display) {
       fprintf(stderr,​ "​failed to create display!\n"​);​       fprintf(stderr,​ "​failed to create display!\n"​);​
-      al_destroy_timer(timer);​ 
       return -1;       return -1;
    }    }
  
-   bouncer ​= al_create_bitmap(BOUNCER_SIZEBOUNCER_SIZE); +   caret = al_create_bitmap(CARET_WIDTHCARET_HEIGHT); 
-   if(!bouncer) {+   if(!caret) {
       fprintf(stderr,​ "​failed to create bouncer bitmap!\n"​);​       fprintf(stderr,​ "​failed to create bouncer bitmap!\n"​);​
       al_destroy_display(display);​       al_destroy_display(display);​
-      al_destroy_timer(timer);​ 
       return -1;       return -1;
    }    }
  
-   ​al_set_target_bitmap(bouncer);+   ​al_set_target_bitmap(caret);
    ​al_clear_to_color(al_map_rgb(255,​ 0, 255));    ​al_clear_to_color(al_map_rgb(255,​ 0, 255));
    ​al_set_target_bitmap(al_get_backbuffer(display));​    ​al_set_target_bitmap(al_get_backbuffer(display));​
Ligne 100: Ligne 74:
  
    ​if(!event_queue) {    ​if(!event_queue) {
-      ​fprintf(stderr,​ "​failed to create event_queue!\n"​);​ +       fprintf(stderr,​ "​failed to create event_queue!\n"​);​ 
-      al_destroy_bitmap(bouncer); +       ​al_destroy_bitmap(caret); 
-      al_destroy_display(display);​ +       ​al_destroy_display(display);​ 
-      ​al_destroy_timer(timer);​ +       ​return -1;
-      ​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_register_event_source(event_queue,​ al_get_timer_event_source(timer));​ 
-   ​al_register_event_source(event_queue,​ al_get_mouse_event_source());​ 
    ​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();​
-   ​al_start_timer(timer);​ 
  
-   DATA data;+   init_DATA(&​data, display, caret);
  
-   thread_1 ​= al_create_thread(Func_Thread, &​data);​ +   thread ​= al_create_thread(progressBar, &​data);​ 
-   ​al_start_thread(thread_1);+   ​al_start_thread(thread); 
 +     
 +   ​al_rest(10.0);​ /* Loading some awesome datas !! */ 
 +     
 +   //​al_set_thread_should_stop(thread);​ /* appelée implicitement par l'​instruction ci-dessous */ 
 +   ​al_join_thread(thread,​ NULL); 
 +     
 +   ​al_destroy_thread(thread);​ 
 +   ​dest_DATA(&​data);
  
-   al_lock_mutex(data.mutex); +   al_destroy_bitmap(caret); 
-   while (!data.ready){+   al_destroy_display(display)
 +   ​al_destroy_event_queue(event_queue);​
  
-      al_wait_cond(data.cond,​ data.mutex);+   ​return 0; 
 +}
  
-   } +static void *progressBar(ALLEGRO_THREAD *thr, void *arg) 
-   ​al_unlock_mutex(data.mutex);+
 +   ​ALLEGRO_TIMER ​      ​*timer ​      = NULL; 
 +   ​ALLEGRO_EVENT_QUEUE *event_queue = NULL; 
 +   ​ALLEGRO_EVENT ​      ev;
  
-   al_lock_mutex(data.mutex); +   DATA *data  = (DATA*arg
-   data.modi_X = true+   float num   = 2.0
-   data.ready ​ ​= ​false; +   float xpos  ​= ​PADDING;
-   ​al_unlock_mutex(data.mutex);+
  
-   thread_2 = al_create_thread(Func_Thread,​ &data); +   if (!data->​display || !data->​caret{ 
-   al_start_thread(thread_2);+      ​fprintf(stderr, "args are NULL!\n"​); 
 +      return NULL; 
 +   }
  
-   al_lock_mutex(data.mutex); +   timer = al_create_timer(1.0 / FPS); 
-   while (!data.ready){ +   if(!timer) { 
- +      ​fprintf(stderr"​failed to create timer!\n"​); 
-      ​al_wait_cond(data.conddata.mutex);+      return NULL; 
 +   }
  
 +   ​event_queue = al_create_event_queue();​
 +   ​if(!event_queue) {
 +      fprintf(stderr,​ "​failed to create event queue!\n"​);​
 +      return NULL;
    }    }
-   al_unlock_mutex(data.mutex); +   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(1)+   ​while(!al_get_thread_should_stop(thr))
    {    {
-      ALLEGRO_EVENT ev; 
       al_wait_for_event(event_queue,​ &ev);       al_wait_for_event(event_queue,​ &ev);
 +      // if(ev.type == ALLEGRO_EVENT_TIMER) /* ne peut être autre chose */
  
-      ​if(ev.type == ALLEGRO_EVENT_TIMER{ +      ​al_draw_bitmap(data->​caret,​ xpos, SCREEN_H-PADDING-CARET_HEIGHT,​ 0); 
-         redraw ​true+ 
-      ​+      xpos +num
-      else if(ev.type =ALLEGRO_EVENT_DISPLAY_CLOSE{+      if (xpos >SCREEN_W-PADDING)
          ​break;​          ​break;​
-      } 
-      else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { 
-         ​break;​ 
-      } 
-      if(redraw && al_is_event_queue_empty(event_queue)) { 
-         ​redraw = false; 
  
-         ​al_lock_mutex(data.mutex);​ +      ​al_flip_display();​
-         float X = data.posiX;​ +
-         float Y = data.posiY;​ +
-         ​al_unlock_mutex(data.mutex);​ +
-          +
-         ​al_draw_bitmap(bouncer,​ X, Y, 0); +
- +
-         al_flip_display();​ +
-      }+
    }    }
-   ​al_destroy_thread(thread_1);​ 
-   ​al_destroy_thread(thread_2);​ 
  
-   ​al_destroy_bitmap(bouncer);​ 
    ​al_destroy_timer(timer);​    ​al_destroy_timer(timer);​
-   ​al_destroy_display(display);​ 
    ​al_destroy_event_queue(event_queue);​    ​al_destroy_event_queue(event_queue);​
- +   return ​NULL;
-   return ​0;+
 } }
- 
-   ​static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg){ 
- 
-   DATA *data  = (DATA*) arg; 
-   float num   = 0.1; 
- 
-   ​al_lock_mutex(data->​mutex);​ 
- 
-   bool modi_X = data->​modi_X;​ 
-   ​data->​ready = true; 
-   ​al_broadcast_cond(data->​cond);​ 
- 
-   ​al_unlock_mutex(data->​mutex);​ 
- 
-   ​while(!al_get_thread_should_stop(thr)){ 
- 
-      al_lock_mutex(data->​mutex);​ 
-      if(modi_X) 
-         ​data->​posiX += num; 
-      else 
-         ​data->​posiY += num; 
-      al_unlock_mutex(data->​mutex);​ 
- 
-      al_rest(0.01);​ 
- 
-   } 
- 
- 
-   ​return NULL; 
-   } 
 </​file>​ </​file>​
- 
  
 ==== Détails ==== ==== Détails ====
  
-TODO : full C (no C++)+<code c> 
 +typedef struct s_data  
 +
 +   ​ALLEGRO_MUTEX ​  ​*mutex;​ 
 +   ​ALLEGRO_DISPLAY *display; 
 +   ​ALLEGRO_BITMAP ​ *caret; 
 +} DATA;
  
-<code c>    +void init_DATA(DATA *data, ALLEGRO_DISPLAY *display, ALLEGRO_BITMAP ​ *caret) 
-class DATA{+
 +   data->mutex   ​= al_create_mutex();​ 
 +   data->​display = display; 
 +   ​data->​caret ​  = caret; 
 +}
  
-   ​public:​ +void dest_DATA(DATA ​*data
- +
-      ALLEGRO_MUTEX ​*mutex; +   ​al_destroy_mutex(data->mutex); 
-      ALLEGRO_COND ​ *cond; +}
-      float          posiX; +
-      float          posiY; +
-      bool           ​modi_X;​ +
-      bool           ​ready;​ +
- +
-   DATA(: mutex(al_create_mutex()),​ +
-            ​cond(al_create_cond()),​ +
-            posiX (0), +
-            posiY (0), +
-            modi_X(false),​ +
-            ready (false) ​{+
- +
-   ~DATA(){ +
- +
-      ​al_destroy_mutex(mutex);​ +
-      ​al_destroy_cond(cond);​ +
- +
-   } +
- +
-};+
 </​code>​ </​code>​
  
-First we create a classwhich is going to contain the data we want to exchange between ​threads.+On crée une structure qui contient les ressources partagéeselle 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).
  
-<code c>​ALLEGRO_MUTEX *mutex</code> +//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.//
- +
-Here we declare a mutex (mutual exclusion). In essence it's a flag that's supposed to tell other threads to leave some data alone. You can't set it directly, though. This should become clear why. +
- +
-<code c>​ALLEGRO_COND ​ *cond;</code> +
- +
-Here we declare a condition. It is sort of a sign that can asynchrously be picked up by other threads. ​Again you'll have to use functions to use it.+
  
 <code c> <code c>
-      float          posiX; +   ​thread = al_create_thread(progressBar&data); 
-      float          posiY; +   al_start_thread(thread); 
-      bool           ​modi_X;​ +     
-      bool           ​ready;</​code>​ +   al_rest(10.0); /* Loading some awesome datas !! */
- +
-**posiX**, and **posiY** are the X, and Y coordinates of our bitmap, **modi_X** and **ready** are two flags that we're going to use later on. +
- +
-<code c> +
- +
-   DATA() : mutex(al_create_mutex()), +
-            cond(al_create_cond()), +
-            ​posiX ​(0)+
-            posiY (0), +
-            modi_X(false),​ +
-            ready (false) {} +
-  +
-   ~DATA(){ +
-  +
-      al_destroy_mutex(mutex); +
-      al_destroy_cond(cond);​ +
-  +
-   } +
 </​code>​ </​code>​
  
-Class'​s constructor,​ using an initialization list. Followed by the destructor. //Note: you cannot make an instance of this object on global scope, because ​**al_create_mutex** and **al_create_cond** would be called before **al_init()**.//+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).
  
-<code c>   ​ALLEGRO_THREAD ​     ​*thread_1 ​   = NULL; +**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**.
-   ​ALLEGRO_THREAD ​     ​*thread_2 ​   = NULL; </code>+
  
-Then inside the **int main()** function we create a pointer to an **ALLEGRO_THREAD**,​ an opaque structure representing a threadA thread is like a program running separately.+C'est une fonction très simple que reprend des notions des articles précédentsSachez 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]].
  
-<code c>DATA data;</​code>​ +On remarquera seulement ceci :
- +
-After the Allegro initialization process, since it's using some allegro functions, we create our object **data**. +
- +
-<code c>​thread_1 = al_create_thread(Func_Thread,​ &​data);​ +
-al_start_thread(thread_1);</​code>​ +
- +
-Here we create our thread and immediately the data is sent. **Func_Thread** is a pointer to a function and **&​data** is the address of our recently created object. And since when a thread is created, it is initially in a suspended state, we need to call **al_start_thread(thread_1)** to begin its actual execution. +
- +
-So, you have to write your own function that matches this prototype:​ +
- +
-<code c> static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg) </​code>​ +
- +
-Whatever you do inside this functions is going to be running on a different thread. +
- +
-Next lines get technical, but can be seen as 1 unit:+
  
 <code c> <code c>
-   al_lock_mutex(data.mutex);​ +   al_set_target_bitmap(al_get_backbuffer(data->​display)); /* new context */
-   ​while ​(!data.ready)+
-  +
-      al_wait_cond(data.cond,​ data.mutex);​ +
-  +
-   } +
-   ​al_unlock_mutex(data.mutex);+
 </​code>​ </​code>​
  
-Firstlywe lock the mutexthis is how we tell Allegro that one of our many threads is going to use a shared resource.\\ +En effetchaque thread possède son propre contextc'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 ​principalil faut le transmettre via notre structure et prévenir notre nouveau ​thread.\\ 
-An analogy would be shouting "//it's my turn now!//"​. If this thread ​is the first one to shoutit gets the mutex.\\ +Si votre jeu termine sur un **ASSERT**, c'est certainement du à une variable du context qui est à **NULL**, donc faites attention !
-If another ​thread ​has locked the mutex firstthis thread ​will have to wait until that particular thread unlocks it.\\ +
-**while (!data.ready){** First of allwe need to check out if the flag ready is true, because in case it is, we don't need to wait for any condition, since for some reason the second thread previously created was faster than the parent thread and it's ready.\\ +
-In case ready isn't true **al_wait_cond** will unlock the previously locked mutex and pauses this thread until the condition is signaled.\\ +
-The function will return when cond is signaledacquiring the lock on the mutex in the process. Finally since **al_wait_cond** acquired the mutex again, we need to call **al_unlock_mutex(data.mutex)** to unlock the mutex. ​+
  
-al_wait_cond can return for other reasons other than the condition becoming true (e.g. the process was signalled). If multiple threads are blocked on the condition variable, the condition may no longer be true by the time the second and later threads are unblocked. Remember not to unlock the mutex prematurely.+==== Amelioration ====
  
-Now you should understand the reason a **while** loop was put around **al_wait_cond**;​ and a variable ​named **ready** was put into the **DATA** class.+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.
  
-<code c>  +On modifie alors notre fonction qui dépend plus du timer :
-   ​al_lock_mutex(data.mutex);​ +
-   ​data.modi_X = true;        +
-   ​data.ready ​ = false; ​      +
-   ​al_unlock_mutex(data.mutex);​ +
-  +
-   ​thread_2 = al_create_thread(Func_Thread,​ &​data);​ +
-   ​al_start_thread(thread_2);​ +
-  +
-   ​al_lock_mutex(data.mutex);​ +
-   while (!data.ready){ +
-  +
-      al_wait_cond(data.cond,​ data.mutex);​ +
-  +
-   } +
-   ​al_unlock_mutex(data.mutex);​+
  
-</​code>​ +<code c> 
- +static void *progressBar(ALLEGRO_THREAD *thr, void *arg) 
-When we come to this point its because our first thread is already done. Now we're going to create and start our second thread, so first we lock the mutex, and change the flag **modi_X** to true, the first thread received this flag as false so the first thread will be changing the Y axis of the bitmap, and the second thread, since it will receive it as true, will change the X axis. We also set our ready flag to false again since we need to do the same process we did with the first thread. +{
- +
-After that, we proceed to create our second thread, as you can see we're using the same function (**Func_Thread**),​ for that reason we changed the flag **modi_X** to true otherwise we could create another thread function which changes the X value, but we would need to rewrite all the code again just to change a little bit of the code and that is not a good practice. +
- +
-After starting the second thread we need wait until is done, so we do the same we did with the first one. Now before we move on, let's see what it's doing the **Func_Thread** function. +
- +
-<code c>  +
- +
-static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg){ +
- +
    DATA *data  = (DATA*) arg;    DATA *data  = (DATA*) arg;
-   ​float ​num   0.1; +   ​float ​pbar_1pc ​= (SCREEN_W-2*PADDING)/100.0
-  +   int goon 1;
-   ​al_lock_mutex(data->mutex); +
-  +
-   bool modi_X ​data->​modi_X; +
-   ​data->​ready = true; +
-   ​al_broadcast_cond(data->​cond);​ +
-  +
-   ​al_unlock_mutex(data->​mutex);​ +
-  +
-   ​while(!al_get_thread_should_stop(thr)){ +
-  +
-      al_lock_mutex(data->​mutex);​ +
-      if(modi_X) +
-         ​data->​posiX += num; +
-      else +
-         ​data->​posiY += num; +
-      al_unlock_mutex(data->​mutex);​ +
-  +
-      al_rest(0.01);​ +
-  +
-   } +
-  +
-  +
-   ​return NULL; +
-   } +
-</​code>​+
  
-The data you sent using **al_create_thread()** is received by this function as a void pointer data type **(void *arg)**. So the first thing we need to do inside here is convert our object back to a DATA datatype, which is our original class. ​+   ​al_set_target_bitmap(al_get_backbuffer(data->​display));
  
-<code c> DATA *data  = (DATA*) arg; </​code>​ +   while(!al_get_thread_should_stop(thr) && goon
- +   {
-With some typecasting it's done. Remember that data now it's a pointer to a DATA object so we'll use **data->​example** instead of **data.example**. +
- +
-<code c> float num   = 0.1; </​code>​ +
- +
-This is the value we're going to add to our X or Y axis. +
- +
-<code c> al_lock_mutex(data->​mutex);​ </​code>​ +
- +
-Again, we lock the mutex since we're going to use shared resources. +
- +
-<code c> bool modi_X = data->​modi_X;​ </​code>​ +
- +
-So, here we're creating a bool variable , and assigning it a value. As you can see, when we created our DATA object, this value is initialized as false, so the <​code>​bool modi_X</​code>​ variable of this thread, if it's the first thread created, is going to be false. We're doing this because we need to tell the first thread to modified the X value and the second thread to modified the Y value, so the thread which receive **modi_X** (Modify X) as true, is going to do exactly that, modified the X Axi, and the thread which receive **modi_X** as false, is going to modified the Y axis. +
- +
-<code c> data->​ready = true; </​code>​ +
- +
-This is the second flag of our data object, we need to use this, because our parent thread needs to wait until we have assigned the **modi_X** value. Since we're working with threads we can never assume the order or speed in which two things in parallel happen. So this flag along with the cond struct, allow us to tell our parent thread, when it can move on. +
- +
-<code c>​al_broadcast_cond(data->​cond);​ </​code>​ +
- +
-After assigning the threads **modi_X** to false, we change the ready flag to true, to tell to the parent thread that it doesn'​t need to wait, because the thread is ready, but in case it's waiting, it'll be waiting for this cond, so we also broadcast our condition so it can move on, with this line: +
- +
-<code c> al_unlock_mutex(data->​mutex);​ </​code>​ +
- +
-Finally we unlock our mutex to tell Allegro that we're not going to be using the data object anymore so other threads can start using it. +
- +
-Ok, we need to go back to our parent thread, and make a little review: +
- +
- +
-<code c> +
-   ​thread_1 = al_create_thread(Func_Thread,​ &​data);​ +
-   ​al_start_thread(thread_1);​ +
-  +
-   ​al_lock_mutex(data.mutex);​ +
-   ​if ​(!data.ready) +
-  +
-      al_wait_cond(data.cond, data.mutex)+
-  +
-   ​al_unlock_mutex(data.mutex);​ +
-  +
-   ​al_lock_mutex(data.mutex);​ +
-   ​data.modi_X = true; +
-   ​data.ready ​ = false; +
-   ​al_unlock_mutex(data.mutex);​ +
-  +
-   ​thread_2 = al_create_thread(Func_Thread, ​&data); +
-   al_start_thread(thread_2);​ +
-  +
-   ​al_lock_mutex(data.mutex);​ +
-   if (!data.ready) +
-  +
-      al_wait_cond(data.cond,​ data.mutex);​ +
-  +
-   ​al_unlock_mutex(data.mutex);​ +
- +
-</​code>​ +
- +
-=== Quick Review === +
- +
-  * In line 1 we create the thread. +
-  * In line 2 we start the thread. +
-  * in line 4 we lock our mutex because we're going to check if **ready** it's true +
-  * In case it's true that means our second tread for some reason haven'​t initialized its **mosi_X** bool variable, so we need to wait. +
-  * In line 6 we start waiting. +
-  * Ok our first thread has broadcast the conditional that means he is ready. +
-  * In line 8 we unlock the mutex because we're not going to use it anymore. +
-  * In line 10 we lock the mutex again, we need to change our variables because we're about to create another thread, and if we don't change the flags the second thread would be a complete mess. +
-  * In line 11 we change our **modi_X** variable to true, that way our second thread it's going to modified the X value of our bitmap. +
-  * In line 12 we change back our ready variable to false again, because now we need to do the same with the second thread, we need to know when it's ready. +
-  * In line 13 we unlock everything again. +
- +
-And the next lines does everything again, but for the second thread. +
-=== Two Threads Running=== +
-At this point we have two threads running! +
- +
-Now what? +
- +
-Well those threas are changing the posi_X and posi_Y variables, so if you don't hurry up and draw the bitmap you're not going to see anything. +
- +
-But before drawing anything let's see how these threads are changing these variables:​ +
- +
-<code c> +
- +
-   ​while(!al_get_thread_should_stop(thr)){ +
- +
       al_lock_mutex(data->​mutex);​       al_lock_mutex(data->​mutex);​
-      ​if(modi_X+      ​al_wait_cond(data->​cond,​ data->​mutex); 
-         ​data->posiX += num+      ​al_draw_bitmap(data->caret, data->​percent*pbar_1pc,​ SCREEN_H-PADDING-CARET_HEIGHT,​ 0)
-      ​else +      ​if (data->percent == 100) 
-         data->posiY +num;+         ​goon ​0;
       al_unlock_mutex(data->​mutex);​       al_unlock_mutex(data->​mutex);​
-  
-      al_rest(0.01);​ 
-  
-   } 
-  
-  
-   ​return NULL; 
  
- </​code>​ +      ​al_flip_display();​
- +
-We're back to our **Func_Thread()** function. +
- +
-After initializing some values and telling to our parent thread that we're ready, the thread enter in a loop. A very simple loop that you should understand very well. To keep it simple we're not going to create another timer but just use <​code>​al_rest()</​code>​ which allow us to rest the thread some seconds, otherwise this loop would run so fast that we wouldn'​t be able to see our bitmap on the screen. +
- +
-<code c>​while(!al_get_thread_should_stop(thr)) </​code>​ +
- +
-The same way this thread talked with the parent thread to tell it was ready using the **cond** variable, this function **al_get_thread_should_stop(thr)** allows the parent thread to talk with this thread, but this time we use it to tell our thread that must stop. +
- +
-If **al_get_thread_should_stop(thr)** returns true, the thread will stop, how can we make this function to return true?, we'll see it later on. **thr** it's the other value that **Func_Thread** receives. It's nothing more than a pointer to the current thread. +
- +
-So we're done with the thread function. Now let's advance in our parent thread. +
- +
-<code c>    while(1) +
-   { +
-      ALLEGRO_EVENT ev; +
-      al_wait_for_event(event_queue,​ &ev); +
-  +
-      if(ev.type == ALLEGRO_EVENT_TIMER) { +
-         ​redraw = true; +
-      } +
-      else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { +
-         ​break;​ +
-      } +
-      else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { +
-         ​break;​ +
-      } +
-      if(redraw && al_is_event_queue_empty(event_queue)) { +
-         ​redraw = false; +
-          +
-         ​al_lock_mutex(data.mutex);​ +
-         float X = data.posiX;​ +
-         float Y = data.posiY;​ +
-         ​al_unlock_mutex(data.mutex);​ +
-          +
-         ​al_draw_bitmap(bouncer,​ X, Y, 0); +
-  +
-         al_flip_display();​ +
-      }+
    }    }
-   ​al_destroy_thread(thread_1);​ 
-   ​al_destroy_thread(thread_2);​ 
-  
-   ​al_destroy_bitmap(bouncer);​ 
-   ​al_destroy_timer(timer);​ 
-   ​al_destroy_display(display);​ 
-   ​al_destroy_event_queue(event_queue);​ 
  
 +   ​return NULL;
 +}
 </​code>​ </​code>​
  
-This shouldn'​t be anything new. As you can see in line 17, 18, 19 and 20, we are creating two variables and initializing them with the corresponding value, this is to clarify, that you should keep locked the mutex the less possible, ​**al_draw_bitmap()** take much more time than just creating and initializing two **float** variables, for that reason we're doing this.+La fonction restera bloquée sur **al_wait_cond** tant que le thread parent n'a pas appelé ​**al_broadcast_cond**.
  
-<code c> +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 :
-al_destroy_thread(thread_1);​ +
-al_destroy_thread(thread_2);​ </​code>​+
  
-And finally we're destroying the threads when we exit the program. This implicitly performs **al_join_thread** which at the same time implicitly calls **al_set_thread_should_stop** that way **al_get_thread_should_stop(thr)** will return true and the thread ​is going to stop.+<code C> 
 +    ​al_set_thread_should_stop(thread); 
 +    al_lock_mutex(data.mutex); 
 +    al_broadcast_cond(data.cond);​ 
 +    al_unlock_mutex(data.mutex);​ 
 +</​code>​
  
-//Note: This is a very simple example to help you get the ideanormally you don't need to initialize first thread then another one and so onyou can do whatever you want, though ;).//+Maintenant que vous savez utiliser les threadsvous savez pratiquement tout ce qu'il y à savoir sur le core d'​allegrodans 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]]
allegro/threads.1324328084.txt.gz · Dernière modification: 2011/12/19 21:54 par mrhide