gestion des variables dans une dll multithread ?

gestion des variables dans une dll multithread ? - C++ - Programmation

Marsh Posté le 24-05-2005 à 13:20:31    

Salut,
 
voila mon problème:
 
Je suis entrain de coder une interface java pour réaliser des taches variées (ex: encodage video, audio, (de)multiplexage, cryptage, etc.).
La plupart de ces opérations sont réalisées par des programmes C/C++. J'interface donc les fonctions C necessaires via la JNI (Java Native Interface, utilisation de fonction C en Java) ce qui nécéssite de recompiler les programmes C/C++ comme des dll. Jusque la pas de problème.
 
Maintenant, j'aimerais pouvoir lancer plusieurs taches identiques en même temps (ex: deux encodages audio) qui vont donc utiliser la même dll.
Chaque thread est lancé depuis Java (java.exe[|javaw.exe]) et donc ils s'executent tous dans le meme process. J'en suit donc revenu a un cas similaire à un prog C/C++ qui génére plusieurs thread, chacun pouvant faire appel "simultanément" à une même fonction d'une dll.
 
1. je suppose que je dois compiler ma dll en multithread. OUI|NON ?
 
2. Comment ca ce déroule en interne au niveau des variables ? Est ce que ca pose un problème de partage si une meme fonction est appellée au même moment par 2 threads du même process ? (je demande ca car je crois que la dll fournit une instance du code par process et non par thread).
 
3. Le prog C/C++ original contient des variables globales qui sont donc partagées entre tous les process qui utilisent la dll (enfin, je crois). Quelqu'un a t'il une idée pour que je puisse gérer une sorte d'environnement par thread (ex: si on peut executer une fonction des que le thread reprend la main et affecter les variables globales avec les siennes) ?
 

Reply

Marsh Posté le 24-05-2005 à 13:20:31   

Reply

Marsh Posté le 20-08-2006 à 00:20:51    

UP car ça m'interesse (même si c'est vieux).

Reply

Marsh Posté le 24-08-2006 à 17:13:48    

1. j'crois que c'est clair :D
 
2. Dans la mesure où le process dispose d'un seul espace d'adressage. Donc les variables globales (statiques ou non) seront accédées par tous les threads à l'identique. Le plus important pour des fonctions utilisées par des threads est qu'elles soient réentrantes, CàD que tout les éléments variables soient locaux (passage de paramètres et variables locales). A partir du moment où tu introduis une variable globale ou dynamique commune à plusieurs thread, tu es obligé de gérer un mutex lecteur/écrivain.
 
3. Pour les variables globales des DLL, tout dépend si elles sont statiques ou pas (et je crois me souvenir que la liste des symboles visibles d'une DLL doit être donnée explicitement). Et pour répondre à ta question, la solution reste à réentrance avec un pointeur sur un contexte (une struct somme toute) alloué dynamiquement lors de la création du thread.
 
Vala, :)

Reply

Marsh Posté le 24-08-2006 à 17:41:54    

TNZ a écrit :

Dans la mesure où le process dispose d'un seul espace d'adressage. Donc les variables globales (statiques ou non) seront accédées par tous les threads à l'identique.

C'est ce que je voulais savoir, et je l'ai vérifié par moi-même.
 
Je vais expliquer mon cas. J'utilise une librairie qui faits des calcul. Celle-ci utilise, en "interne", des variables globales. Pour moi à priori peu importe, du moment que le résultat est bon :)
Je lance le calcul dans un thread, tout va bien. Seulement, je décide d'en lancer un 2ème (dans un autre thread) et là, les résultats sont délirant. C'est dû à ces fameuses variables globales qui ne peuvent pas etre utilisées pour plusieurs calculs en même temps.
Je décide donc, juste pour tester car je n'ai pas eu d'infos claires là-dessus, de créer une DLL (avec les fonctions et les variables globales de la librairie en question) et de charger explicitement (via LoadLibrary et GetProcAddress) cette DLL dans chaque thread. Juste pour voir si elles accèdent au même espace mémoire ou si les variables sont dupliquées. Mais non, j'ai le même problème comme je le craignais.
 
La seule solution que je vois c'est de supprimer ces variables globales de 2 manières :
1) les rendre locales et faire des passages par adresse
2) créer une classe avec variables membre
Mais cette bibliothèque est énorme, pas simple à "gérer" donc je n'enviseage aucune de ces 2 solutions.
 
Sachant que je ne veux pas qu'il y ait de "temps mort" (chaque thread est totalement indépendant et doit tourner "à fond" ) existe-t-il une autre solution que je ne connais pas ?


Message édité par Fouge le 24-08-2006 à 17:43:52
Reply

Marsh Posté le 24-08-2006 à 18:06:27    

Ce que tu dis en fait, c'est que ce n'est pas réentrant ! :D
 
Le coup de la classe avec variables membre revient à gérer un contexte (une struct) unique au thread. Au final, si tu veux des éléments communs à plusieurs thread il n'y a que 2 cas de figure :  
 
1. Non-réentrant
Il faut gérer un mutex d'accès à la ressource (sur la fonction ou la variable au choix)
 
2. Réentrant
Avoir les variables de calculs dans une zone mémoire "réservée" au thread appelant. Typiquement, la pile CàD les variables locales et le passage de paramètres.


Message édité par TNZ le 24-08-2006 à 18:07:07
Reply

Marsh Posté le 24-08-2006 à 18:17:28    

Ok, donc :
- soit j'utilise les mutex, ce qui signifira je suppose, qu'il y aura des "temps mort".
- soit la réentrance, en utilisant l'une des 2 méthodes que j'ai évoqué.
- soit j'ai rien compris à ce que tu m'as dit :D

Reply

Marsh Posté le 25-08-2006 à 06:55:34    

Il y a d'autres options, en fait c'est un chouya plus compliqué.
 
D'une part il y a la découpe logique, partiellement imputable au langage. Pour faire bref, les "globales" requierent un arbitrage. On peut, par vague ordre décroissant de coût et croissant de siouxerie, soit faire appel à l'OS - les fameux semaphore/mutex/etc - mais c'est couteux surtout en cas de contention et ça implique le scheduler, soit regler soi-meme l'exlusion par exemple avec des spinlocks, soit utiliser des structures lock-free ou mieux wait-free, ou encore assurer la coherence par l'emploi de primitives atomiques - au sens large. Les deux dernieres options se recoupent et peuvent etre particulierement délicates à mettre en oeuvre :)
 
Mais il n'y a pas qu'une distinction "logique", sur une machine NUMA - disons une babasse avec plusieurs processeurs AMD - il y a effectivement une mémoire locale. Il est donc d'autant plus important de faire attention à la localité. La pile, même sur un OS moisis comme xp, est allouée localement. Après, ça devient plus délicat; une des recettes est sinon d'utiliser VirtualAlloc et/ou de toucher la mémoire à chaque page. Ou les TLS, mais c'est bancal.
 
Ce que je viens d'écrire n'est pas exhaustif mais devrait permettre qques recherches fructueuses sur google.

Reply

Marsh Posté le 25-08-2006 à 09:32:44    

Fouge » t'as tout compris :)

Reply

Marsh Posté le 25-08-2006 à 09:47:18    

tbp> Ok, là ça dépasse mes compétences mais j'avais jeter un oeil, j'vais sans doute apprendre des choses interessantes.
 
Et merci à vous 2 :jap:

Reply

Marsh Posté le 23-09-2009 à 17:59:48    

j'ai eu le même souci mais avec une fonction "non thread-safe", c à dire une fonction non réentrante. il s'agit de la fonction avcodec_open() de libavcodec (projet FFmpeg). je voulais développer une application capable de décoder deux flux vidéo en même temps (deux threads séparés).  
 
Le problème est que l'appel a cet fonction (avcodec_open) pose des problèmes (même avec le mécanisme de l'exclusion mutuelle ça ne fonctionne pas).
 
pour tester vraiment est-ce-que le probleme viens de cette fonction, j'ai séparer les deux threads dans deux programme principaux séparés : Thread1 dans le programme 1, et Thread 2 dans P2. j'ai lancé les deux programme Un après l'autre. le Premier s'exécute bien, le deuxième genère des messages d'erreur prouvenant de la bibliothèque qui englobe la fonction avcodec_open.
 
Etant tt a fait conscient que la modéfication d'une tel fonction de décodage nécessite de revoir tt le fonctionnement de la bibliothèque, c ki est impossible avec le manque de temps que j'ai et mes capacités limitées en C.
 
une idée ou un commentaire de votre part serais sans doute constructif et  très intéressant.  

Reply

Marsh Posté le 23-09-2009 à 17:59:48   

Reply

Marsh Posté le 23-09-2009 à 18:15:48    

Moche. C'est moche. Et à problème moche, solution moche (mochons nous, donc; ok je sors).
Au lieu de threads, utilisez des process+IPC (ou sur un OS digne de ce nom, clone avec les flags qui vont bien); en mappant les données ça ne devrait pas être trop pénalisant, sauf à ce que vous ayez besoin de merger ce que est produit par exemple (vs simplement écrire directement ou il faut).

Reply

Marsh Posté le 23-09-2009 à 22:23:49    

le vrai probleme c'ets d'avoir des fonctions de thread non reentrantes, pas d'utiliser des thread.

Reply

Marsh Posté le 23-09-2009 à 23:08:13    

Une fois isolé, disons dans un process, on se tamponne de l'absence de reentrance; pour rappel iglance7 ne peut/veut pas toucher à la bibliothèque fautive.

Reply

Marsh Posté le 24-09-2009 à 11:54:48    

J'ai eu le même problème avec une bibliothèque de décomposition convexe de géométrie (pour avoir des petites enveloppes convexes de collision pour un moteur physique), j'ai fait comme tbp, j'ai fait un process externe + memmap.

Reply

Marsh Posté le 24-09-2009 à 13:42:43    

tbp: OK j'avais pas vu ce detail. A ce moment oui, process etc

Reply

Marsh Posté le 24-09-2009 à 21:11:10    

Vu que j'ai toujours mon flag sur ce topic, je vous donne la solution que j'ai finalement adopté pour mon problème. La DLL en question, je l'ai dupliqué (autant de fichier que de thread) et chacune était chargée dynamiquement par un thread. Du coup, chaque thread accède à un espace mémoire différent et tout se passe bien malgré les variables globales.
C'était de loin la solution la plus simple et la plus rapide, modifier la librairie n'était pas une solution raisonnable (en terme de temps passé).

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed