FAQ SDLConsultez toutes les FAQ

Nombre d'auteurs : 6, nombre de questions : 67, création le 10 mai 2013 

 
OuvrirSommaireThreads

SDL permet une gestion très simple des threads. Pour créer un thread, il suffit de créer une structure SDL_Thread et de l'initialiser avec la fonction SDL_CreateThread, qui prend comme premier paramètre le pointeur de la fonction à exécuter ainsi qu'un éventuel argument à cette fonction dans le second paramètre.

Il faudra toutefois veiller à passer comme paramètre un pointeur sur une fonction retournant un entier (int) et prenant comme paramètre un pointeur vide (void*). Pour plus d'infos, voir http://jeux.developpez.com/faq/sdl/?page=threads#THREADS_fonction_membre.

 
Sélectionnez
/* Définition de la fonction qui sera lancée par notre thread */
int ThreadFunc(void* Data)
{
    /* On convertit le paramètre dans le type réel voulu par la fonction */
    SDL_Surface* Surf = (SDL_Surface*)Data;

    /* Remplit la surface en rouge */
    SDL_FillRect(Surf, NULL, SDL_MapRGB(Surf->format, 255, 0, 0));

    return 0;
}

/* Déclaration de la structure du thread */
SDL_Thread* myThread;

/* Création du thread ; dès l'appel de cette fonction, le thread commence à s'exécuter */
myThread = SDL_CreateThread(ThreadFunc, MainScreen);

/* À la fin, on peut attendre sur le thread avant de fermer le programme */
int result;
SDL_WaitThread(myThread, &result);
printf("Notre fonction s'est terminée avec le code : %i", result);

À noter que pour utiliser les threads, il faudra inclure l'en-tête « sdl_thread.h » qui n'est pas inclus par défaut dans « sdl.h ».

Créé le 16 mars 2006  par Fiquet

Lorsque l'on termine le programme, il est conseillé d'attendre que toutes les exécutions de nos threads soient bien terminées pour éviter des erreurs néfastes. Pour cela on peut utiliser la fonction suivante :

 
Sélectionnez
void SDL_WaitThread(SDL_Thread* thread, int* status)

Qui est bloquante tant que le thread n'a pas fini son exécution, et qui retourne dans le paramètre status le retour de la fonction que le thread exécutait.

Pour un exemple d'utilisation, voir http://jeux.developpez.com/faq/sdl/?page=threads#THREADS_creer.

Créé le 16 mars 2006  par Fiquet

Pour forcer l'arrêt d'un thread avant que celui-ci ait fini de s'exécuter, on peut utiliser la fonction suivante, qui stoppera le thread passé en paramètre :

 
Sélectionnez
void SDL_KillThread(SDL_Thread* thread)

Cependant, la destruction d'un thread de cette manière peut être dangereuse, car elle fermera brutalement le thread en cours d'exécution, ce qui peut amener à d'éventuelles erreurs et est déconseillé :

 
Sélectionnez
/* Une fonction anodine, mais qui peut cacher des problèmes au sein d'un thread... */
int ThreadFunc(void* Data)
{
    /* Un tableau de surfaces */
    SDL_Surface* SurfTab[100];

    /* Allocation de 100 surfaces */
    for(int i = 0; i < 100; i++)
    {
        SurfTab[i] = SDL_CreateRGBSurface(SDL_SWSURFACE, 400, 400, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
    }

    /* Libération des surfaces */
    for(i = 0; i < 100; i++)
    {
        SDL_FreeSurface(SurfTab[i]);
    }

    return 0;   
}

La fonction ci-dessus est bien écrite et libère correctement la mémoire juste après en avoir alloué, cependant imaginez que l'on stoppe le thread entre les deux boucles : bonjour les dégâts ! Il est donc recommandé d'attendre la fin normale du thread (voir http://jeux.developpez.com/faq/sdl/?page=threads#THREADS_attente).

Créé le 16 mars 2006  par Fiquet

Pour éviter que deux ou plusieurs threads ne modifient par exemple une même donnée en même temps, on peut utiliser avec SDL le principe des verrous d'exclusion mutuelle (mutex).

On peut créer avec SDL un mutex avec la fonction :

 
Sélectionnez
SDL_mutex* SDL_CreateMutex()

Qui retourne une structure de type SDL_mutex.

Ensuite, pour bloquer une section de code momentanément aux autres threads, on peut utiliser la fonction

 
Sélectionnez
int SDL_mutexP(SDL_mutex* mutex)

Qui permet de verrouiller le mutex. Si le mutex a déjà été verrouillé par un autre thread, la fonction reste bloquée jusqu'à ce que le mutex ne soit plus verrouillé.

Enfin, lorsque le code à protéger est terminé, on peut débloquer le mutex grâce à la fonction :

 
Sélectionnez
int SDL_mutexV(SDL_mutex* mutex)

À la fin de l'utilisation du mutex, on peut libérer celui-ci avec la fonction :

 
Sélectionnez
void SDL_DestroyMutex(SDL_mutex* mutex)

Voici un petit exemple pratique illustrant tout ceci :

 
Sélectionnez
/* Déclaration du mutex */
SDL_mutex* myMutex = SDL_CreateMutex();

/* Les deux fonctions suivantes peuvent par exemple travailler sur la surface d'écran,
   mais elles ne vont jamais rentrer en conflit, car si l'une des deux a verrouillé le mutex,
   l'autre attendra jusqu'à ce que celui-ci soit libre. */
int ThreadFuncA(void* Data)
{
    SDL_Surface* MainScreen = (SDL_Surface*) Data

    SDL_mutexP(myMutex);
    /* Traitement sur l'écran... */
    SDL_mutexV(myMutex);
}

int ThreadFuncB(void* Data)
{
    SDL_Surface* MainScreen = (SDL_Surface*) Data

    SDL_mutexP(myMutex);
    /* Un autre traitement sur l'écran... */
    SDL_mutexV(myMutex);
}

/* Déclaration des deux threads dans le programme */
SDL_Thread* myFirstThread  = SDL_CreateThread(ThreadFuncA, MainScreen);
SDL_Thread* mySecondThread = SDL_CreateThread(ThreadFuncB, MainScreen);

/* Fin des threads */
SDL_WaitThread(myFirstThread, NULL);
SDL_WaitThread(mySecondThread, NULL);

/* Destruction du mutex */
SDL_DestroyMutex(myMutex);

Note : on peut aussi utiliser les macros SDL_LockMutex() et SDL_UnlockMutex() définies par SDL, qui appellent les deux fonctions décrites précédemment. Ces dernières ont l'avantage d'avoir un nom un peu plus explicite…

Créé le 16 mars 2006  par Fiquet

En C++, beaucoup sont tentés d'utiliser une fonction membre de classe pour lancer un thread. Malheureusement c'est impossible : fonctions membres et fonctions non membres (ou membres statiques) n'ont pas le même type.

SDL_CreateThread attend une fonction de type int(*)(void*).
Une fonction membre équivalente serait de type int(MaClasse::*)(void*).
Il ne faut en effet pas oublier que pour appeler une fonction membre, il faut une instance de la classe.

La solution couramment mise en œuvre pour contourner ce problème est simple : on passe par une fonction statique, mais celle-ci ne fait que rediriger vers la fonction membre équivalente d'une instance que l'on aura passée en paramètre via l'argument void*.

 
Sélectionnez
// Notre classe perso pour les threads
class Thread
{
public :

    // La fonction statique qui sera passée en paramètre à SDL_CreateThread
    static int GlobalThreadFunc(void* Data)
    {
        // On reconvertit en Thread* le paramètre Data
        Thread* MyThread = reinterpret_cast<Thread*>(Data);

        // Puis on appelle sa véritable fonction de thread
        return MyThread->ThreadFunc();
    }

private :

    // La véritable fonction qui va gérer le thread, qui peut cette fois être membre
    int ThreadFunc()
    {
        // ...
    }
};

// On peut lancer un nouveau thread de cette manière
Thread* NewThread = new Thread;
SDL_CreateThread(&Thread::GlobalThreadFunc, NewThread);
Créé le 16 mars 2006  par Laurent Gomila
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006-2012 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.