#0 

28-02-2007 10:36:55

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

Bonjour

Je voudrait savoir si vous avez un exemple de programme gerant un reseau sans serveur avec raknet. Je voudrait juste pouvoir echanger des données entre deux ordinateurs de maniere simple un exmple de chat ferait l'affaire mais bon au pire j'utise le modele client-serveur mais sa oblige a dire si on veut heberger la partie et tout et tout .

Enfin c'est juste si vous en avez un sous la main, ne vous casser pas la tête.

dark calculator

Hors ligne


#1 

28-02-2007 20:20:51

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

Bonjour

Fais bien la distinction entre le modèle client/serveur des sockets et une architecture logicielle comportant un "serveur".

Dans une partie multi-joueur le plus simple est de proposer une architecture en étoile, cad 1 serveur et des clients. Dans le cas d'un chat cela implique que les messages transitent par le serveur qui va la rediriger vers le client destinataire du message.

Cela n'est pas contraignant, le code du serveur sera exécuté par une des machines (qui du coup héberge la partie), tandis que les autres joueurs exécutent le code client.

Tu auras l'occasion si tu développes la couche réseau (sous forme de chat par exemple) de ton application de te frotter à de bons problèmes de conception et de codage.

N'hésite pas à poser des questions. Tu auras mon soutien si tu t'y attaques.

fred

Hors ligne


#2 

28-02-2007 20:29:11

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

merci de ta reponse

En fait mon jeu se joue uniquement à deux c'est pour ca que je voulais utiliser une architecture sans serveur pour simplifier mais a mon avis sa complique tout ou c'est impossible
Je cherche donc une methode simple d'implementer le reseaux en c++, raknet semble une bonne chose mais la actuellement je n'arrive meme pas a compiler les exemples avec devc++ donc si quelqu'un sait le faire ou connait un tuto qu'il me fasse signe sa m'aiderait beaucoup.

voila, a+.

dark calculator

Hors ligne


#3 

01-03-2007 01:34:29

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

Bonsoir

ah ben là c'est déjà plus simple!

Une méthode simple consiste à choisir 1 poste "serveur" et 1 poste "client" (disons respectivement le premier crée une partie, avec des parametres de configuration)

Avec les sockets, et de facon très concrète, le serveur va ouvrir une socket serveur, et le client va se connecter au serveur. Je ne vais pas t'expliquer les sockets et TCP/IP, il existe des solutions pour les manipuler dans tous les langages sérieux (et évidemment en C++ avec toutefois le bémol que ceci est dépendant du système pour ce langage).

Une fois la socket ouverte il n'y a "plus qu'à" échanger entre les programmes en écrivant et en lisant les sockets...

C'est une réponse bas niveau, au cas où tu voudrais t'attacher à ta propre implémentation.
Si tu n'es pas familiarisé avec les sockets, je te conseille de les apprivoiser en java au travers de petits tests pour mettre en place tes protocoles de discussion. Après en C++ pour l'intégration avec d'autres outils (typiquement un moteur graphique...).

Concernant Raknet, et d'après ce que j'en ai compris de leur présentation, il s'agit d'un moteur de communications UDP, c'est-à-dire en mode non connecté, avec pertes donc. C'est ce qui est utilisé pour les chats, les communications vocales et tout ce qui supporte de la perte mais le moins de gigue possible (la gigue est la variation du délai de transmission, concrètement ca déforme la voix, un peu à la manière de l'effet doppler). Elle propose aussi des fonctionnalités RPC (Remote Procedure Call), et donc offre une abstraction très poussée du réseau en permettant au programmeur de faire comme s'il appelait les fonctions directement mais dans une application distante ! Les amateurs noteront également les fonctionnalités de sécurisation, de routage et de multicast. Sans faire une étude approfondie de leur produit on peut se douter qu'il s'agit là d'une offre très complète.
Peut-etre un peu trop pour tes besoins immédiats.

Autant il est vrai qu'un projet sérieux (rien d'offensant, j'entends par là grosse équipe, structure commerciale...) ne peut pas se permettre de réinventer la roue pour chaque composant de son projet, autant un petit projet peut se le permettre et, d'après moi, doit se l'imposer pour maitriser les aspects techniques spécifiques afin de, plus tard, faire les bons choix dans l'utilisation de telle ou telle solution clef-en-main.

Enfin concernant les examples je ne les ai pas essayé mais un conseil: utilise de préférence gcc pour tester des produits au travers des exemples car tu t'épargneras les difficultés inhérentes aux IDE (DevCpp, CodeBlocks et bien sur l'innenarable Visual). La quasi totalité des applications open sources sont compilables avec gcc (je n'ai pas de contre exemple en tete). Par contre les IDE ne sont pas supportés systématiquement, et cela est simplement du au fait que chaque IDE requiert une fichier de configuration, souvent pour pas grand chose. A noter que DevCpp n'utilise rien d'autre que GCC comme compilateur.

Quoi qu'il en soit, que tu mettes les mains dans le camboui ou que tu utilises une bibliothèque tu auras besoin de te poser des questions sur ce que tu échanges entre tes 2 programmes et comment tu l'échanges : en un mot cela s'appelle le protocole.

La couche réseau d'une application demande beaucoup de travail de conception abstraite (sur le protocole) et un peu d'huile de coude pour la partie technique (ca c'est vrai pour tout dans ce métier). Mais il est important de noter que le plus gros du boulot (le protocole encore une fois) ne sera pas fait par une bibliothèque.

si ma réponse te parait non pertinente parce que ce n'est pas ce que tu cherches n'hésite pas à m'en faire part smile

bon courage car il en faut, ainsi que de la persévérance !

fred

PS: je te remercie au passage d'avoir fait référence à RakNet. Je ne cherchais pas de solution pour le réseau pour l'instant, mais je sais que j'en aurai besoin plus tard !

Hors ligne


#4 

01-03-2007 09:59:36

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

Merci beaucoup pour ta reponse tres complete,

J'ai deja travaille un peu sur les reseaux meme si je ne maitrise pas encore tout, en fait l'utilisation des sockets directement en bas niveau peut me convenir mais le probleme vient que je veut que mon programme soit portable sur windows et linux, et je crois que le passage de l'un a l'autre ne ce fait pas facilement, se qui obligerait à programmer la partie reseau deux fois se qui est dommage vu que mon programme est actuellement totalement portable mais bon au pire je serait bien obligé.

Sinon il est vrai qu'utiliser gcc directement est ce qu'il y a de mieu mais sous windows c'est un peu le bazar. Mais je crois que j'ai trouve pourquoi je n'arrivait pas à compiler les exemples, c'est surement du au fait que les librairies qui sont compiler le sont pouyr visual c++ (pas de .a) donc il est normal que ca ne marche pas.

Sinon pour mon protocole il se raprocherat de celui d'un chat classique, puisqu'il enverra des messages textes precede surement d'un octet qui indiquerat une action a effectuer.
Effectivement le RPC doit etre intéréssant mais surement un peu complexe alors qu'un switch peut le faire simplement wink

Voila, donc tu me conseillerait d'utiliser directement les sockets de la librairie standart avec un client et un serveur?

Enfin encore merci pour ta reponse

dark calculator

Hors ligne


#5 

01-03-2007 10:05:24

Eagle4
Membre
Date d'inscription: 19-10-2006
Messages: 18

Nous on vient de faire un jeu jouable à 2 en ligne avec un petit chat avant de lancer la partie (disponible dans peu de temps). Voilà comment ca marche :

Nous avons un serveur à nous, c'est à dire un ordinateur constament branché sur le net et qui à une adresse ip fixe (ou plutot dynamique) grace à DynDns.

Nos clients s'y connecte, et ils peuvent donc se parler sur le chat car ils sont clients. ensuite pour créer une partie, il rentre le meme mot de passe (le serveur vérifie que la partie n'existe pas déjà) et ils peuvent lancer la partie. ils sont alors dans un autre subchannel, mais toujours sur les memes port/ip.

C'est un jeu de reflexion alors on a pas besoin d'un super ping, mais il est vraisemblable que si le jeu marche enormement, la bande passante du serveur risquerais d'etre lente. Alors on a déjà pensé à un autre système, les gens se connecte toujours au meme serveur, mais lorsqu'un des gars créer une partie, alors il devient serveur et son ip est enregistré sur notre serveur donc si un client arrive sur notre serveur, il telecharge la liste des ip qui ont créer leurs serveur. Du coup dans le jeu, la bande passante est celle des 2 clients, bien plus rapide car pas partagé.

C'est cette technique qui est utilisé dans COD ou autre et ca marche pas mal (niveau Gameplay je parle)...

Hors ligne


#6 

01-03-2007 10:21:22

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

C'est surement ce qu'il y a de mieu cela oblige a avoir un serveur donc un prdinateur en permanence connecte au net se que je n'ait pas pour l'instant. Mais si un jour j'en ait un ce serait pas mal en plus ca permet les mise a jour direct et plein d'autre avantage mais bon...

Enfin l'utilisation client/serveur est apparament indispensable la seule question qui reste est le portage linux/windows auquel je tient.

a+

dark calculator

Hors ligne


#7 

01-03-2007 11:12:02

katsankat
Membre
Date d'inscription: 24-02-2007
Messages: 43

Salut,

La portabilité n'est pas un problème. C'est vrai que l' implémentation des sockets BSD n'est pas conforme sur windows, mais la source est portable avec seulement quelques directives pré-processseur, en C/C++.

Si ton jeu se joue à deux, c'est simple: un des deux fait le serveur (il se met en écoute sur un port) et l' autre fait le client (il se connecte à ce port). C'est exactment le même principe que lorsque je me connecte à un site web: je suis le client et je me connecte sur une machine qui fait serveur, en l' occurence un serveur HTTP.

Hors ligne


#8 

01-03-2007 23:30:24

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

Bonsoir,

Concernant la portabilité il y a pas trop de soucis, c'est du travail en plus mais comme le dit Katsankat il s'agit de quelques directives précompilateur (attention, il n'y a pas de miracle, un peu de code est spécifique windows, et un peu de code est spécifique posix (linux, unix, etc...) mais on dit que c'est portable puisque la compilation fera la différence entre les plateformes par les directives de précompilateur. La portabilité n'est pas remise en cause dès l'instant où on fournit le code pour chaque plateforme...).

Concernant la notion de serveur dans le paradigme client/serveur, ne confond pas avec les serveurs des jeux comme world of warcraft. On parle ici d'une machine qui sert de point d'ancrage pour la partie, de point de rendez-vous. C'est le programme du jeu, dans un mode serveur dans ce sens où il est lancé en premier et qu'il attend des clients (en l'occurence 1 seul) pour communiquer.

J'ai lu dans un autre post de ce forum qu'il existait aussi GNet pour faire du réseau : http://www.gnetlibrary.org/

fred

Hors ligne


#9 

02-03-2007 00:01:33

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

Rebonjour,

Bon j'ai un peu réfléchit et je vais utiliser les fonctions de la librairie standard mais les fonction comme recv() bloque la boucle (attente d'un message) se qui est pas genial pour le rendu il faut donc faire du multithreading ce ou je ne suis pas tres fort mais bon un fork pourrait sufire : Il y a 2 joueurs. chaque voit son plateau et celui de l'autre mais il n'interargit que sur le sien. Des qu'il fait une action un message est envoyé a l'autre joueur pour qu'elle aparaisse sur son pc. le probleme est la reception du message. Je pense donc faire un fork, le processus pere s'occupe du joueur "qui joue et de l'affichage" et le fils de la recuperation des messages et d'effectuer les actions sur le second plateu.

Est ce que vous pensez que c'est une bonne idée?

A mon avis je n'est pas ete tres claire, n'hesiter pas a poser des questions

Encore merci a tous vous m'avez ete d'une grande aide. wink

dark calculator

PS: Voila un petit shemas vite fait pour clarifier les chose wink

                          PC1(J1)                                              PC2(J2)
                              |                                                        |   
                             init                                                     init
                              |                                                        |
                            fork                                                    fork
                              |                                                        |
                ----------------------                                     ------------------------
               |                           |                                    |                             |
      init plateau 1(J1)       init plateau 2(J2)               init plateau 1(J2)       init plateau 2(J1)
               |                            |                                    |                            |
            boucle                    boucle                           boucle                    boucle
          -> drawall()               ->attent message           -> drawall()               ->attent message
          ->attente action          +effectue action             ->attente action          +effectue action
               +envoie mesage            concerne                 +envoie mesage            concerne
                        |                               |                                   |                              |
                        |                               ---<--------<--------<------                             |
                         -------------->-------------------->-------------------------->--------------

Hors ligne


#10 

02-03-2007 03:13:50

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

Déjà première chose : à mon sens tu te poses les bonnes questions, c'est bien !  smile

recv est bloquant en effet, et c'est normal : cette fonction va récupérer les données qu'un autre programme lui envoie. Elle ne peut pas faire autrement qu'attendre d'avoir des données pour les restituer, elle est donc dépendante du bon vouloir de l'autre programme. Il s'agit d'une communication asynchrone.

Comme tu l'as donc remarqué tu auras besoin de dissocier l'écoute réseau de la gestion de l'interface. En réalité pour être tout à fait précis, tu as normalement au minimum 3 fils d'exécution : l'interface graphique, le calcul et les communications inter-processus. L'IHM est dépendante des actions de l'être humain et se doit d'être réactive (sinon l'interface se frise, phénomène bien connu des programmes java mal codés). En ce sens c'est assez proche de la problématique du réseau : l'humain envoie des données sous forme de clics, de touches frappées et ce de manière asynchrone.

Pour revenir à ta proposition, je t'arrête tout de suite quand tu veux faire un fork, et ce pour 3 raisons :

Premièrement ceci est lourd (comparé à du multithreading). Tu vas charger 2 fois ton programme, et une grande partie du code n'est pas utilisé dans les 2 instances, donc surcharge évidente de ton système (par rapport à du multithreading).

Deuxièmement tu auras à faire communiquer le père et le fils... Là tu as le choix entre les sockets, les flux (pipe) nommés, les sémaphores (purement signalétique et pour la synchronisation) ou la mémoire partagée (beaucoup d'emmerdes, et pas portables du tout du tout !!).

Troisièmement tu aurais intérêt à apprendre à faire du multithreading, notamment parce que c'est pas compliqué et parce que cela te feras toucher du doigts des aspects de synchronisation et de gestion des accès concurrentiels tout à fait primordiaux en informatique.


J'en viens à présent à un semblant de solution.
Je suis de nature trop ambitieuse quand je concois des solutions informatique. Mais ca n'est pas génant, on a toujours la possibilité de revenir à quelque chose de moins subtil et (souvent) de plus fiable à mettre en place.

Les communications réseaux sont asynchrones, cela veut dire que tu auras à gérer les communications entrantes au fil de l'eau. On peut voir une comm entrante comme un événement (des données associées à un traitement). Et là j'en reviens au protocole dont je t'ai parlé l'autre jour.
Tu auras donc un thread dédié à l'écoute sur la socket. Celui va boucler autour de la fonction read(). A chaque fois qu'il recoit un message, tu appelles une fonction de traitement de ce message. Tu peux imaginer qu'un code en début de message te permette de choisir la fonction à appeler (un switch fait l'affaire). Et voilà, c'est tout ! Tu as un système de communication événementiel. A toi ensuite de coder les fonctions pour que ton jeu tienne compte de ce qui arrive du réseau.

Un petit exemple est toujours utile :

Tu as développé un jeu de plateau, du genre bataille navale ou échecs.
Un joueur A fait un coup.
Ceci a des conséquences *locales* comme modifier les données de la partie et raffraichir l'IHM.
Et ceci a des conséquences *distantes* : le coup est transmis au joueur B.
B va donc recevoir par son module réseau un événement "A a tiré en C5" (pour la bataille navale wink ).
Cet événement va avoir des conséquences sur les données de la parties et sur l'IHM. On voit ici l'analogie entre l'IHM et le réseau.

Pour parler un peu en terme de conception, on est très vite tenté pour bien faire les choses de reprendre le modèle MVC (modèle, vue, controleur). Ce paradigme fait la distinction entre les données (le modèle), la vue (l'IHM) et le controleur (les boutons de l'IHM ou des événements comme ce que le réseau envoie).

     M------V
       \    /
         C------ Humain
          \------ Réseau

L'humain et le réseau vont déclencher des actions sur le contrôleur, qui va modifier le modèle en conséquence pour ensuite demander à la vue de mettre à jour avec la nouvelle version du modèle.

L'élément Réseau reste donc à construire. En résumé je te propose de retenir ceci:
  - mettre en parallèle (dans un thread pour accéder à la meme zone de mémoire)
  - faire une boucle d'écoute autour de read(), déclenchant du code à chaque réception d'un message
  - coder les différentes fonctions associées à chaque message (ce code n'est pas très différent de celui qui est associé à l'interface graphique => mise en commun du code, plus robuste et plus facilement maintenable)

Voilà, encore une fois je n'ai pas pu m'empecher de faire long.
Si je n'ai pas été assez clair, n'hésite pas à intervenir à nouveau smile

sur ce, bonne nuit !  tongue
fred

Hors ligne


#11 

02-03-2007 10:47:54

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

Merci beaucoup pour tes reponses toujours aussi complete,

Il ya 2 ou 3 choses que je n'ait pas compris

coder les différentes fonctions associées à chaque message (ce code n'est pas très différent de celui qui est associé à l'interface graphique => mise en commun du code, plus robuste et plus facilement maintenable)


Par ceci tu veut dire que ce soit le meme code qui detruit un bateau sur son plateau et celui de l'adversaire du genre int detruire( tableau* t, int bateau)?
Si c'est ca c'est se que j'ai fait.

Ensuite combien de thread je vais avoir en fin de compte ?
2 ou 3, je ne sait pas trop, car 2 suffirait mais peut etre que 3 est mieu ?
Mais qu'est ce que je m'est dans chacun : un avec le read, ca s'est bon, et ensuite je separe les routines d'affichage du traitement des evenement utilisateurs ?
(Ce modele me convient assez, logique, carre, claire, chaque thread son boulot smile)

Sinon j'ai quelque question sur le vocabulaire, qu'est ce que l'IHM, a quoi serve reelement les semaphores ?
Et aussi est ce que utiliser les meme donnes dans 2 threads posent de reel probleme ?Par exemple utiliser le device irrlicht dans les 3 threads posent ils un probleme ?

Voila je te remercie encore pour tes reponses qui m'aident vraiment

dark calculator

Hors ligne


#12 

02-03-2007 12:24:44

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

Dans un cas simple voici ce que ca pourrait donner :

Code:

struct modele {
  int largeur, hauteur;
  bool[][] tirs;
  /* etc */
};

void tirer(modele* data, int x, int y) {
   if (x>=0 && y>=0 && x<largeur && y<hauteur)
     data->tirs[x][y] = true;
   else
     std::cerr << "tir incorrect !" << std::endl;
}

pour le réseau, tu auras un message : un tir
ceci va être traité par une méthode "tir" qui appellera tirer() sur le modèle qui va bien (celui de l'adversaire) avec les paramètres reçus depuis le réseau.

Code:

modele* adversaire;
void tir(int x, int y) {
   tirer(adversaire, x, y);
}

// Drapeau de contrôle de la boucle d'écoute :
bool conditionArret;
// A exécuter dans un thread :
void ecoute() {
  while(!conditionArret) {
    // 1) Récupération du message :
    std::string message;
    /* "lire un message" avec read(); */

    // 2) Traitement du message :
    switch((char) message[0]) {
      case 'T':
         {
            int x, y;
            sscanf(message, "T %f %f", &x, &y);
            tir(x, y);
         }
         break;
      default:
         std::cout << "Message inconnu : '" << message << "'" << std::endl;
    }
  }
}

pour l'interface homme-machine, en supposant que tu sois en mesure de réagir à la position sur une grille où l'humain cliquera cela reviendra à déclencher la méthode tirer sur le modèle local avec les positions récupérées du click.

En C++ tu auras tout intérêt à créer une classe Modele reprenant la structure de données et intégrant les fonctions comme tirer sous forme de méthode. Tu auras ainsi une meilleure lisibilité de ton code ainsi qu'une meilleur fiabilité (il vaut mieux invoquer une méthode sur un objet qu'appeler une fonction avec pour paramètre un pointeur sur une structure, le compilateur faisant un certain boulot de vérification).

Les sémaphores sont une technique de communication inter-processus, utilisés sous unix. Je t'en ai parlé plus pour ta culture générale, ils n'ont pas d'utilité dans un programme multithreadé. Le principe est d'utiliser des compteurs, que l'on incrémente quand on en a besoin et que l'on décrémente. Ceci permet notamment de synchroniser des processus (compteurs partagés sur la taille des données en commun par exemple, verrous binaires en limitant le compteur à 1).

Hors ligne


#13 

02-03-2007 14:09:36

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

salut

En C++ tu auras tout intérêt à créer une classe Modele reprenant la structure de données et intégrant les fonctions comme tirer sous forme de méthode.


Effectivement se serait pas mal il va falloir que je le fasse.

Bon je vais me mettre au multithreading mais se qui me casser les pieds avec, c'est le passsage de parametre et surtout leur recuperation mais bon je vais m'en remettre

Sinon est ce que je dois faire attention a que tous les threads n'accedent pas a la meme donne en meme temps ou ca n'a pas d'importance ?

Hors ligne


#14 

02-03-2007 15:46:47

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

dark calculator :

Bon je vais me mettre au multithreading mais se qui me casser les pieds avec, c'est le passsage de parametre et surtout leur recuperation mais bon je vais m'en remettre


En fait le passage de parametres à ton thread dépend un peu du système de threads. Mais quoi qu'il en soit puisque tu partages le meme espace mémoire tu peur agir de la facon suivante :

Je te propose un exemple en java (non pas parce que c'est ce que tu vas utiliser mais c'est un des plus simples que je connaisse et surtout c'est celui dont je me souviens le mieux, voici :

Code:

public class MonThread extends Thread {
  int param1;
  string param2;

  public MonThread(int p1, string p2) {
    param1 = p1;
    param2 = p2;
  }

  public void run() {
     // code execute par le thread.
     for (int i=0; i<p1; i++) {
       System.out.println(param2);
     }
  }

  public static void main(string[] args) {
     MonThread fil = new MonThread();

     // lancement du thread
     fil.start();

     // on attend qu'il ait fini son execution
     fil.join();
  }
}

Voici en 2 mots ce que ca fait :
En java tout est codé sous forme de classes, les threads y compris. En l'occurrence on va donc développer une classe héritant de java.lang.Thread. Le code exécuté par le thread sera celui contenu dans la méthode run(). Cette méthode a le meme role que main pour un programme. Puisqu'on travaille avec des objets, la méthode run() a accès aux champs de son objet. Ici 2 champs param1 et param2 qui sont affectés par le constructeur.
Enfin on lance le thread sur une instance de cette classe avec la méthode start() (cf. main exemple)

Comprends bien ceci :
le multithreading a l'avantage de multiplier les fils d'exécution tout en partageant la même zone de mémoire (les variables, pointeurs etc). Aussi les différents threads vont accéder aux mêmes variables. Donc pour "configurer" un thread (ce que tu appelles à juste titre passage de parametres par analogie avec le main), il faut renseigner des variables et ensuite lancer le thread. Celui-ci "doit" consulter ces variables pour etre configuré. La condition est naturellement que ce code ait accès à ces variables (problème de portée). En C il suffit d'utiliser des variables globales, en C++ comme en Java et tout langage POO, une instance de classe est le plus naturel et le plus pertinent.

----

dark calculator :

Sinon est ce que je dois faire attention a que tous les threads n'accedent pas a la meme donne en meme temps ou ca n'a pas d'importance ?


Ceci est de ta responsabilité !
En fait ce problème n'a d'impact que sur la viabilité de tes données. Si 2 threads accèdent en meme temps à la même variable et que les opérations effectuées ne sont pas atomiques, il y a un risque important que ces opérations se fasse en même temps et que cela ait des conséquences facheuses. Tu ne maitrises pas l'ordonnancement, c'est à dire l'ordre dans lequel les threads sont executés (je rappelle qu'on ne peut pas faire tourner 2 threads au meme moment sur le meme cpu (ou du moins sur le meme coeur de cpu...)). C'est le "systeme" (la JVM pour java, le noyau en C/C++) qui décide de quel thread s'execute.

Ainsi pour eviter les problèmes (et quand cela est indispensable) on cherche à protéger certaines sections de codes de toute préemption (cad de perte du CPU au profit d'un autre thread). Ces sections de codes sont nommées "sections critiques" car elle doivent être protégées. Généralement on code cela avec des verrous, selon le schéma suivant :
1) acquisition du verrou
2) section critique
3) libération du verrou

Tu comprendras facilement que ces aspects là sont très rapidement pénibles. Mais également souvent indispensable. Aussi quand on développe un programme parallele il convient à un moment donné de déterminer quel sections de codes sont critiques (de point de vue accès concurrentiel) et comment il faudra les protéger.

Dans ton cas je ne pense pas "a priori" qu'il y en ait.
Ceci est du à 2 choses :
Les actions critiques sont celles qui modifient le modèle. Celui-ci ne sera a priori pas modifié en meme temps par 2 joueurs (tu peux interdire les actions du joueur tant que l'adversaire n'a pas joué, et finalement ca crée des sections critiques).
Les actions de modifications seront pour la plupart atomiques. On dit qu'une action est atomique quand elle ne peut pas etre interrompue par le système, exemple : affectation, incrémentation opération binaire ou unaire. Cela dépend un peu d'un systeme / langage à l'autre, mais ca reste un concept commun à toute l'informatique parallèle.


Je pense que tu as compris par toi même qu'un programme, même modeste, a très vite besoin de beaucoup de petites briques. Cependant il est indispensable de commencer par des programmes modestes pour maîtriser les concepts élémentaires et réussir à produire quelque chose qui marche.
Après le passage à une plus grande échelle n'est souvent qu'une question de temps (donc de moyens).

bon courage

fred

Hors ligne


#15 

02-03-2007 19:28:20

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

Donc si j'ai bien compris il faut une section critique et tout le tralala uniquement si l'ordre dans lequel les données sont modifié est important ou si les changements peuvent etre instable.

Par exemple est ce que le code suivant peut devenir instable :

Code:

int* i[99]; 
int nbr = -1;

void *th1(void* ptr)
{
    while (nbr!=98)
    {
        nbr++;
        i[nbr] = new int(5);
    }
}



void *th2(void* ptr)
{
    while (1)
    {
        for (int j=0;j<=nbr;j++)
        {
            printf("%i",*i);
         }
         printf("\n");
    }
}

int main()
{
     int t1 = create_thread(th1);
     int t2 = create_thread(th2);
}

Si j'ai bien compris ce code (qui est totalement inutile) est instable car dans th1 il est possible que nbr soit incremente et que juste apres soit lancé lz printf sur le i[nbr] qui n'existe pas encore.

D'ailleur comment ce passe le fait que nbr peut avoir changer entre le debut et la fin de la boucle (je suppose comme il fait le test a chaque boucle qu'il va tenir compte de la nouvelle valeur de nbr)

Encore merci(je n'en finit plus de te dire merci wink)
Ton aide mes precieuses et m'aide beacoup a clarifier les choses

dark calculator

Hors ligne


#16 

02-03-2007 21:36:51

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

L'accès au CPU se fait de la facon suivante :
Thread 1  ***-----*******-------------****--*******
Thread 2  ----****----------*********-----**----------

On ne maitrise pas le basculement entre chaque thread, c'est le système qui prend ces décisions.

Tout ce que tu sais c'est qu'une opération atomique ne sera pas interrompue.

Dans ton cas qu'est-ce que cela donne ?

Le thread 1 va boucler jusqu'à avoir initialiser les 98 pointeurs.

A chaque boucle il fait :
- un test
- une incrementation
- la création d'un pointeur sur une nouvelle zone de mémoire
- une affectation

La préemption (le basculement vers l'autre thread) peut se faire entre chaque étape.

Et donc le thread 2 peut reprendre son execution dans chacune de ses étapes.

Ce qui est important de vérifier est la cohérence des données. Si la préemption se fait entre les étapes 2 à 4 tu auras un problème : le compteur de pointeurs valides (nbr) aura augmenté sans que le nouveau pointeur n'ait été créé ou stocké dans le tableau. Ce qui aboutira "peut-etre" à une segmentation fault si tu atteints le dernier pointeur sans que celui-ci ait été initialisé. (ici pas de segmentation fault puisque tu affiches une valeur pointée sous forme d'entier, il y a toujours une valeur en mémoire, mais par contre ca ne sera pas la valeur attendue ! donc c'est un bug).

Tu repéreras les sections critiques en analysant le code qui amène à des états incohérents, instable dans ton vocabulaire. Ce sont des états intermédiaires, indispensables mais que l'on ne peut utiliser dans un autre contexte.

Une solution ici consisterait à faire l'initialisation et l'affectation avant l'incrémentation, comme ca tu seras sûr que le thread 2 n'accèdera pas à une zone mémoire non initialisée.

Enfin pour la question de la boucle n'oublie jamais comment fonctionne une boucle for. Ici tu as utilisé la syntaxe ultra classique qui consiste à balayer un tableau connaissant sa taille. Mais il ne faut pas oublier que la condition de fin (le deuxieme statement dans le for) est exécuté à chaque pas. Ce qui fait que nbr n'a pas à être fixe. Ceci peut être interessant pour des boucles complexes où les conditions de fins peuvent être influencées par l'exécution de la boucle, son contenu...

voilà voilà smile

fred

Hors ligne


#17 

03-03-2007 10:24:50

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

dark calculator, si tu veux un exemple de thread tout fait, jette un oeuil sur mon tuto sur les threads posix, ca prends un poil plus de temps a installer, mais tu sera sur de leur comportement

http://www.irrlicht.fr/forum/viewtopic.php?id=92#top

les mutex et semaphore sont les primitives standart des threads posix

Hors ligne


#18 

03-03-2007 18:08:29

dark calculator
Abonné
Date d'inscription: 25-02-2007
Messages: 153

Merci beaucoup pour les pthreads mais je l'ai deja inclus a mon programme(dommage que je n'est pas vue ton post plus tot), je les ait " redecouvert " hier car je les avait utilise sous linux mais je savais pas qu'ils etaient compatible windows.
D'aileur pthread ne vas pas bientot etre remplace par l'equipe de gcc car avec les nouveaux proccesseur double coeur il n'etait pas tres perfomant ?

Enfin peut etre que je dit une betise mais il me semble avoir lu sa.

Enfin bon sinon mon prog avance grace a vous, je vais essaye de faire attention au ressource memoire partage mais normalement ca dvrait etre bon (enfin j'espere).

a+

dark calculator

Dernière modification par dark calculator (03-03-2007 18:09:34)

Hors ligne


#19 

03-03-2007 18:45:00

ptitfred
Membre
Lieu: Paris
Date d'inscription: 17-02-2007
Messages: 21
Site web

Il est vrai que l'introduction des CPU multi coeurs va imposer de revoir un peu la programmation parallèle.

Il est très courant de voir un jeu ne pas tirer profit des CPU multi coeurs, le code n'étant pas prévu pour ça !

Quoi qu'il en soit, tu n'as pas à t'inquiéter pour les pthreads, ils ont fait leurs preuves !

Il y a peut-être d'autres solutions portables, voire chez "boost" s'ils n'ont pas leur propres threads. Qt doit aussi proposer ses threads (ma mémoire me joue peut-être des tours, à vérifier).


N'hésite pas à partager ton travail, notamment quand tu as des doutes sur les questions de synchronisation. Avec les threads il faudra aussi que tu penses à les arrêter avant de quitter ton programme. C'est un travail équivalent à la libération de ressources.

Bonne continuation,
fred

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
883 membres
1429 sujets
11121 messages
Dernier membre inscrit: Saidov17
79 invités en ligne
Aucun membre connecté
RSS Feed