Thread 2 (linux et windows)

Proposé par smeagol

le 15 December 2006 à 14h 42mn 12s

14766 visualisations



je reposte mon header qui permet de gérer des thread sout linux et windows.

Code:

#ifndef MY_THREAD_H

#define MY_THREAD_H



//Ces macros servent à uniformiser certaines fonctionnalités entre Windows et Linux.

#ifdef WIN32 //macro version Windows



#   include <windows.h>



    //definition des threads



#   define callback_t                       unsigned long __stdcall



#   define thread_t                         HANDLE*

#   define thread_create(thrd, fct, param)  thrd = new HANDLE;\

                                            *(thrd) = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(fct),(param),0,NULL)

#   define thread_delete(thrd)              CloseHandle(*(thrd));\

                                            delete (thrd);

#   define thread_wait_close(thrd)          WaitForMultipleObjects(1, *thrd, TRUE, INFINITE)



    //definition des semaphores et mutex



#   define mutex_t                          CRITICAL_SECTION

#   define mutex_init(mutex)                InitializeCriticalSection((mutex))

#   define mutex_lock(mutex)                EnterCriticalSection((mutex))

#   define mutex_unlock(mutex)              LeaveCriticalSection((mutex))

#   define mutex_delete(mutex)              DeleteCriticalSection((mutex))



#   define semaphore_t                      HANDLE

#   define semaphore_init(sema, max, place) ((sema) = CreateSemaphore(NULL, (max), (place), NULL))

#   define semaphore_lock(sema)             WaitForSingleObject((sema), INFINITE)

#   define semaphore_unlock(sema)           ReleaseSemaphore((sema), 1, NULL)

#   define semaphore_delete(sema)           CloseHandle(sema)



#else //macro version Linux



    //definition des threads



#   include <pthread.h>



#   define callback_t                       void * 



#   define thread_t                         pthread_t*

#   define thread_create(thrd, fct, param)  (thrd) = new pthread_t;\

                                            pthread_create((thrd), NULL, (fct), (param))

#   define thread_delete(thrd)              delete (thrd);

#   define thread_wait_close(thrd)          pthread_join(*thrd, NULL)



    //definition des semaphores et mutex



#   include <semaphore.h>



#   define mutex_t                          pthread_mutex_t *

#   define mutex_init(mutex)                (mutex) = new pthread_mutex_t;\

                                            pthread_mutex_init ((mutex), NULL)

#   define mutex_lock(mutex)                pthread_mutex_lock((mutex))

#   define mutex_unlock(mutex)              pthread_mutex_unlock((mutex))

#   define mutex_delete(mutex)              pthread_mutex_destroy((mutex));\

                                            delete ((mutex))



#   define semaphore_t                      sem_t*

#   define semaphore_init(sema, max, place) (sema) = new sem_t;\

                                            sem_init ((sema), (max), (place))

#   define semaphore_lock(sema)             sem_wait((sema))

#   define semaphore_unlock(sema)           sem_post((sema))

#   define semaphore_delete(sema)           sem_destroy((sema));\

                                            delete ((sema))



#endif



#endif

Pour commencer un thread est un outils capable de lancer des exécution parallèles de code dans un même programme.

Code:

                 ________________
                |                |
                | Code Principal |
                |________________|
                        |
                    intruction...
                        |                ________________
                        |               |                |
                execution du thread  -> | Code du thread |
                        |               |________________|
                        |                       |
                        |                       |
                    intruction...          intruction...
                        |                       |
                        |                       |
                        |-----------------------|
                        |
                   Fin du prog

Comme le dit l'autre tuto les thread sont à éviter tant qu'une exécution séquentiel est possible.
Mais pour un moteur 3d je dirais qu'ils sont conseillés surtout si il y a un moteur physique.
Je m'explique vous avez remarqué que les processeurs récents on tous des dual-corps. Et bien chaque thread s'exécute sur un corps différent se qui permet un gain énorme en performance.

Bien sur les threads ne sont pas a utilisés à la légère a cause des ressources critiques. Vous avez bien plusieurs processeurs mais qu'une seul Mémoire RAM. Et si 2 threads tentent d'accéder à  la même partie de la mémoire et bien il risque de ne pas avoir les résultats voulus. Et même de faire planter le programme.
Je ne vais pas vous faire un court sur les risques des ressources critiques et vous demande de me croire sur parole que ceci est très dangereux pour un programme fiable.

Pour éviter de rentrer a plusieurs dans une zone critique il existe des fonctions de mise en attente. Les mutexs et le semaphores. Vous me dites "Je peux le faire moi même avec une variable global booléenne" Mais je vous répond que non car imaginé que pendant que vous tester votre variable et que vous changer d'état la variable, le processeur permute de thread. Vous risquez de rentrer à deux dans la zone critique et la de planter ou créer des erreurs dans votre programme.

Voici un petit programme simple avec l'utilisation des mutexs.

Code:

#include <iostream>
#include "mythread.h"

//creation du mutex
mutex_t mut;
mutex_t io;

void trace(int x)
{
    //Vue que trace est appeler par un thread 
    //et qu'elle utilise les entrers sorties
    //il faut utiliser un autre mutex
    
    //ATTENTION voici la zone critique seule un thread à le droit 
    //d'utiliser l'ES
    
    mutex_lock(io);
        printf("x = %d\n", x);
    mutex_unlock(io);
    
    //Fin de zone critique
    //un thread en file d'attente peut rentrer
    
    return;
};

callback_t maFonc(void* x)
{
    //recastage de x
    int* intX = (int*)
    
    
    //ATTENTION voici la zone critique seule un thread à le droit 
    // de modifier la veleur de X
    
    mutex_lock(mut);
        //multiplication de x par lui même
        *intX = *intX * *intX;
    mutex_unlock(mut);
    
    //Fin de zone critique
    //un thread en file d'attente peut rentrer
    
    trace(*intX);
    
    return NULL;
};

int main()
{
    //Création de x
    int x;
    
    //initialisation du mutex
    mutex_init(mut);
    mutex_init(io);
    
    //Creation des threads
    thread_t thrd[10];
    int i;
    for(i = 0; i < 10; i++)
        thread_create(thrd[i], maFonc, &x);
    
    //attente de la fermeture des threads
    for(i = 0; i < 10; i++)
        thread_wait_close(thrd[i]);
    
    //destruction des threads et du mutex
    for(i = 0; i < 10; i++)
        thread_delete(thrd);
        
    mutex_delete(mutex);
    mutex_delete(io);
    
    printf("\n\nX Final = %d\n", x);
    
    return 0;
};

Par contre il y a une règle de syntaxe qui ne va pas dans se programme. Il est important de s'imposer que seul les fonctions callback et celle qui créer le thread doivent utiliser des mutexs. deonc le code serait mieux ainsi:

Code:

#include <iostream>
#include "mythread.h"

//creation du mutex
mutex_t mut;

void trace(int x)
{
    printf("x = %d\n", x);
    
    return;
};

callback_t maFonc(void* x)
{
    //recastage de x
    int* intX = (int*)
    
    
    //ATTENTION voici la zone critique seule un thread à le droit 
    // de modifier la veleur de X
    
    mutex_lock(mut);
        //multiplication de x par lui même
        *intX = *intX * *intX;
        trace(*intX);
    mutex_unlock(mut);
    
    //Fin de zone critique
    //un thread en file d'attente peut rentrer
    
    return NULL;
};

int main()
{
    //Création de x
    int x;
    
    //initialisation du mutex
    mutex_init(mut);
    
    //Creation des threads
    thread_t thrd[10];
    int i;
    for(i = 0; i < 10; i++)
        thread_create(thrd[i], maFonc, &x);
    
    //attente de la fermeture des threads
    for(i = 0; i < 10; i++)
        thread_wait_close(thrd[i]);
    
    //destruction des threads et du mutex
    for(i = 0; i < 10; i++)
        thread_delete(thrd);
        
    mutex_delete(mutex);
    
    printf("\n\nX Final = %d\n", x);
    
    return 0;
};

Pour finir les semaphores.

Je ne vais pas poster de code car il fonctionne comme les mutexs. À la seul différence c'est que, dans le create_semaphore, 'max' représente le nombre maximum de thread qui on le droit de rentrer dans la zone critique.
Et place est le nombre au quels sont déjà rentrer dans le semaphore.

je mexplique:

Code:

create_semaphore(sema, 10, 2);

10 processus auront le droit de rentrer dans le semaphore mais comme place = 2 seulement 8 pourront après l'initialisation. Donc si on fait un ...

Code:

create_semaphore(sema, 10, 10);

Aucun thread ne pourra rentrer dans la zone critique tant que l'on n'a pas fait un semaphore_unlock.

Astuce: create_semaphore(sema, 1, 0); est donc un mutex

---------------------------------------------------------------------------------------

PS : Je ne dit pas que cet header est parfait et sans bug mais je l'utilise personnellement et je n'est jamais eu un seul bug dessus.

Voilà à vos questions


#1 

16-12-2006 09:21:29

Jerry Kan
Habitué
Date d'inscription: 21-11-2006
Messages: 265

super bien détaillé ton tuto,
je pense que je vais me servir de ton header,

merci smile

Hors ligne


#2 

04-01-2007 10:54:10

Jerry Kan
Habitué
Date d'inscription: 21-11-2006
Messages: 265

Je suis en train d'essayer ca depuis un moment ce matin,
en fait je n'arrive pas a faire marcher le tout (pas testé sous linux), je suis sous code::block (meme erreur sous dev) , j'avoue que je suis pas tres habitué a windows (beuurk ^^ ) donc je rate peut etre un truc tout simple

j'ai créé un le .h et le .cpp comme tu le décrit, je clique sur le truc pour compiler et la :


Code:

Project   : Console application
Compiler  : GNU GCC Compiler (called directly)
Directory : C:\Documents and Settings\laurent\Bureau\th2\
--------------------------------------------------------------------------------
Switching to target: default
Compiling: main.cpp
In file included from main.cpp:2:
mythread.h:29: error: expected constructor, destructor, or type conversion before '=' token
mythread.h:29: error: expected `,' or `;' before '=' token
main.cpp: In function `long unsigned int maFonc(void*)':
main.cpp:23: error: cannot convert `CRITICAL_SECTION' to `_CRITICAL_SECTION*' for argument `1' to `void EnterCriticalSection(_CRITICAL_SECTION*)'
main.cpp:27: error: cannot convert `CRITICAL_SECTION' to `_CRITICAL_SECTION*' for argument `1' to `void LeaveCriticalSection(_CRITICAL_SECTION*)'
main.cpp:32: warning: converting to non-pointer type `long unsigned int' from NULL
main.cpp: In function `int main()':
main.cpp:41: error: cannot convert `CRITICAL_SECTION' to `_CRITICAL_SECTION*' for argument `1' to `void InitializeCriticalSection(_CRITICAL_SECTION*)'
main.cpp:51: error: invalid conversion from `void*' to `void* const*'
main.cpp:51: error:   initializing argument 2 of `DWORD WaitForMultipleObjects(DWORD, void* const*, BOOL, DWORD)'
main.cpp:57: error: `mutex' undeclared (first use this function)
main.cpp:57: error: (Each undeclared identifier is reported only once for each function it appears in.)
main.cpp:62:3: warning: no newline at end of file
Process terminated with status 1 (0 minutes, 1 seconds)
10 errors, 1 warnings

la premiere des erreurs corresponds a la ligne :

Code:

*(thrd) = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(fct),(param),0,NULL)

éventuellement tu aurait un zip d'un exemple comprenant les sources et le .cbp (ou le .dev) ?

Dernière modification par Jerry Kan (04-01-2007 10:57:16)

Hors ligne


#3 

04-01-2007 11:21:04

kedu
Modérateur
Date d'inscription: 23-09-2006
Messages: 155

Je n'ai pratiquement pas de notion en c++ donc ne m'en veut pas si je me plante mais pour ta ligne d'erreur :

*(thrd) = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(fct),(param),0,NULL)

Est-ce que CreateThread est une méthode quelconque de la classe ou le constructeur de la classe ?

Si c'est un constructeur, il faudrait mettre new après le =. (si new est la bonne syntaxe en c++)

Désolé par avance si je suis à côté de la plaque :-/

Hors ligne


#4 

04-01-2007 11:29:00

Jerry Kan
Habitué
Date d'inscription: 21-11-2006
Messages: 265

a priori non, puisque c'est un exemple qui ne comprends pas de classe,
comme c'est une ligne de la grosse macro du .h, je suis un peu perdu,

mais je suis arrivé a faire autrement en utilisant la librairie pthread.dll, et avec laquelle on peut porter les threads linux sans changement de code, je suis en train de mettre a jour mon tuto (l'autre tuto) pour expliquer comment faire

Hors ligne


#5 

04-01-2007 11:50:24

kedu
Modérateur
Date d'inscription: 23-09-2006
Messages: 155

Ah oui intéressant ça ! Si ça peut éviter de s'arracher les cheveux à faire des conversions (très passionnantes) de code...

Hors ligne


#6 

04-01-2007 14:10:37

smeagol
Membre
Date d'inscription: 30-10-2006
Messages: 34

Je ne vois pas trop. je ne peut pas te faire un exemple pour windows car je ne l'est plus. Mais ces macro son utilisé en simplifier dans mk_socket (disponible sur l'upload). Et sa fonctionne sous win et linux. Enfin bon si tu trouve la source bug. envoie ta version corriger du header.

Hors ligne


#7 

05-01-2007 10:38:57

Jerry Kan
Habitué
Date d'inscription: 21-11-2006
Messages: 265

je pense qu'une partie du problème viens du formatage fait par un copier coller

les lignes du type :

Code:

#   define semaphore_delete(sema)           sem_destroy((sema));\

                                            delete ((sema));

doivent etre remplacée par

Code:

#   define semaphore_delete(sema)           sem_destroy((sema)); delete ((sema));

parce que pour une raison obscure, lors du copier coller, le  caractere "\" a perdu son "pouvoir" a faire poursuivre sur la meme ligne

quand j'arrive a refaire marcher le tout je fait un zip et je l'upload

Dernière modification par Jerry Kan (05-01-2007 10:39:30)

Hors ligne


#8 

05-01-2007 13:13:24

Jerry Kan
Habitué
Date d'inscription: 21-11-2006
Messages: 265

voila j'ai terminé le debug, ca fonctionne (testé windows et linux),
j'ai uploadé le zip avec le .h et les deux exemples ici

attention, j'ai modifié plusieurs choses dans le .h et .cpp donc ca marche un peu différemment qu'a l'origine, notamment coté utilisation des pointeurs,

Dernière modification par Jerry Kan (05-01-2007 13:14:58)

Hors ligne


#9 

05-01-2007 23:09:00

Copland
Modérateur
Lieu: ZarbiLand
Date d'inscription: 22-09-2006
Messages: 657
Site web

Merci beaucoup pour le zip, je charge ça car je vais en avoir besoin...pas dans l'immédiat mais c'est prévu à mon programme donc un grand merci pour votre aide smile.


Config : I5 2400, ATI HD6870 1Go DDR5, 4Go DDR3.
Single Boot : Windows Seven.

Hors ligne


#10 

13-01-2007 03:28:49

Copland
Modérateur
Lieu: ZarbiLand
Date d'inscription: 22-09-2006
Messages: 657
Site web

Waow !! Que du bonheur vraiment merci, je viens de l'utiliser sous Windows pour gérer ma physique et ça a l'air de bien fonctionner !
Encore merci à vous pour se code et ses explications smile.


Config : I5 2400, ATI HD6870 1Go DDR5, 4Go DDR3.
Single Boot : Windows Seven.

Hors ligne


Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
882 membres
1429 sujets
11119 messages
Dernier membre inscrit: LiseBuisson96
19 invités en ligne
Aucun membre connecté
RSS Feed