Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

thread et passage de parametre

21 réponses
Avatar
Bruno Causse
bonjour,

pour les threads je ne sert d'une strategie a la "java"

une classe abstraite pure (Interface)

class Runnable{
public:
virtual void run() = 0;
}

un classe thread tres simple (un simple wrapper des threads POSIX)

class POSIXThread {
pthread_t _threadID;

public :

POSIXThread(Runnable *ptr);
virtual ~POSIXThread() {};

void start();

protected:

static void* threadfunction(void * pt);
Runnable* _threadObj;
};

class RXEngine : public Runnable {

.../...

//variable d'instance
RXParamThread param;

//metode d'instance
void dansUneFonctionInit();
virtual void run();

};

void RXEngine::dansUneFonctionInit() {

param.init();
RXThread thread(this);
thread.start();

}

void RXEngine::run() {

//ici je peu utiliser la variable d'instance param

}

il y a t'il une facon plus elegante des passer la variable "param"

merci
--
Bruno Causse

10 réponses

1 2 3
Avatar
Sylvain SF
James Kanze wrote on 12/09/2008 09:44:

pourquoi pas:



void* POSIXThread::threadFunction(void* ref){
Runnable* runnable = static_cast<Runnable*>(ref);
runnable->run();
delete runnable
return NULL;
}



Pour commencer, parce que tu ne peux pas passer l'adresse d'une
fonction membre à pthread_create:-).



nous sommes d'accord!
je ne sais pas précisemment ce qu'est la définition de la classe
POSIXThread ici mais j'imaginais:

static void* POSIXThread::threadFunction(void*);

l'appelant réalisant:

Runnable* runnable = new RunnableChild(someParams);
pthread_t child = NULL;
pthread_create(&child, NULL,
POSIXThread::threadFunction, runnable);

Plus généralement, ceci suppose que le runnable est toujours
alloué dynamiquement.



oui, je supposais cela, je n'imagine pas qu'il ne le soit pas.

Et qu'il ne contient pas de données qu'on veut propager vers
vers le thread appelant (suite à un join).



de données ?... ayant une "sémantique de valeur" ? ;)
le thread peux appeler une méthode de l'appelant avec un bloc
résultat copié à la réception par cet appelant. (pratiquement
cela nécessitera un pool de réception géré par cet appelant).

Et qu'on se contente d'ignore les exceptions. («Sweep them under
the rug », dirait-on en anglais. Je ne connais pas d'équivalent
aussi imagé en français.)



non pas de les ignorer, seulement de ne pas écrire sciemment un
code qui les violeraient (pthread_exit par exemple).

pour un contrôle fin, Runnable devra proposer des "suspend",
"resume", "finalize", ..., toutes asynchrones et toutes
respectueuses de la terminaison correcte du thread.

purger proprement des threads à durée de vie courte ne me
parait pas tordu ?!



Il marche pour un cas de figure bien précis.



peut être bien.

Sylvain.
Avatar
James Kanze
On Sep 13, 6:28 pm, Sylvain SF wrote:
James Kanze wrote on 12/09/2008 09:44:
>> pourquoi pas:



>> void* POSIXThread::threadFunction(void* ref){
>> Runnable* runnable = static_cast<Runnable*>(ref);
>> runnable->run();
>> delete runnable
>> return NULL;
>> }



> Pour commencer, parce que tu ne peux pas passer l'adresse
> d'une fonction membre à pthread_create:-).



nous sommes d'accord!
je ne sais pas précisemment ce qu'est la définition de la
classe POSIXThread ici mais j'imaginais:



static void* POSIXThread::threadFunction(void*);



l'appelant réalisant:



Runnable* runnable = new RunnableChild(someParams);
pthread_t child = NULL;
pthread_create(&child, NULL,
POSIXThread::threadFunction, runnable);



C'est ce que j'imagine aussi. Or que la norme est claire : le
linkage fait partie du type de la fonction. Alors, ou bien le
système déclare un surcharge de pthread_create qui prend une
function avec linkage "C++" (mais je n'en connais pas qui le
font), ou bien, il faut que la fonction dont on passe l'adresse
ait un linkage "C". Et une fonction membre, même static, ne peut
pas avoir un linkage "C".

> Plus généralement, ceci suppose que le runnable est toujours
> alloué dynamiquement.



oui, je supposais cela, je n'imagine pas qu'il ne le soit pas.



Moi si. Dans le cas d'un thread détaché, par exemple, quelque
chose du genre de ce que fait Boost, on on copie l'objet
fonctionel, me semble très raisonable. Et dans le cas où on
utilse des threads pour la parallelisation d'un calcul, je
verrais bien quelque chose du genre :

Part2Runnable actionPart1 ;
Thread part2( &action ) ;
part2.start() ;
// execute part1 ici...
part2.join() ;

Pourquoi est-ce que je dois avoir besoin des shared_ptr ici ?

> Et qu'il ne contient pas de données qu'on veut propager vers
> vers le thread appelant (suite à un join).



de données ?... ayant une "sémantique de valeur" ? ;) le
thread peux appeler une méthode de l'appelant avec un bloc
résultat copié à la réception par cet appelant. (pratiquement
cela nécessitera un pool de réception géré par cet appelant).



Pratiquement, ça nécessitera aussi des lock supplémentaire, etc.
Parfois, c'est une bonne idée quand même -- j'utilise volentiers
une queue pour la réponse aussi. Mais d'autres fois, on pourrait
préférer utiliser join, avec un valeur de rétour, ou des données
« partagées ».

> Et qu'on se contente d'ignore les exceptions. («Sweep them
> under the rug », dirait-on en anglais. Je ne connais pas
> d'équivalent aussi imagé en français.)



non pas de les ignorer, seulement de ne pas écrire sciemment
un code qui les violeraient (pthread_exit par exemple).



Ce n'est pas ça qui m'intéressait. Je me posais la question sur
leur transmission vers le thread appelant. Parfois, ça peut être
utile que l'exception se propage à travers le join, pour se
rétrouver dans le processus qui a démarrer le thread.

pour un contrôle fin, Runnable devra proposer des "suspend",
"resume", "finalize", ..., toutes asynchrones et toutes
respectueuses de la terminaison correcte du thread.



Ce qui est fort difficile. Ou plutôt, qui exige la collaboration
active de la classe dérivée (pas d'attente sans timeout, par
exemple).

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Sylvain SF
James Kanze wrote on 14/09/2008 11:54:

il faut que la fonction dont on passe l'adresse ait un linkage "C".
Et une fonction membre, même static, ne peut pas avoir un linkage "C".



oui. avec un compilo MS on utilise un modifier propriétaire pour
indiquer cela, avec GCC (ou autre) et les fonctions POSIX, je n'ai
aucune idée du moyen de faire cela, j'ai supposé que cela existait.

Moi si. Dans le cas d'un thread détaché, par exemple, quelque
chose du genre de ce que fait Boost, on on copie l'objet
fonctionel, me semble très raisonable. Et dans le cas où on
utilse des threads pour la parallelisation d'un calcul, je
verrais bien quelque chose du genre :

Part2Runnable actionPart1 ;
Thread part2( &action ) ;
part2.start() ;
// execute part1 ici...
part2.join() ;

Pourquoi est-ce que je dois avoir besoin des shared_ptr ici ?



j'ai pris l'habitude d'utiliser des threads dynamiques y compris
dans ce cas (et d'assembler les morceaux via un pool de l'appelant)
principalement parce que le découpage pour obtenir des temps à peu
près égaux entre les threads et gérer l'appel bloquant à join() me
parait plus incertain.

reste que ton écriture (sobre et efficace) répondrait en effet
parfaitement à des découpages fonctionnels simples.

[...] on pourrait préférer utiliser join, avec un valeur de
rétour, ou des données « partagées ».



oui !

ignorer les exceptions





Ce n'est pas ça qui m'intéressait. Je me posais la question sur
leur transmission vers le thread appelant. Parfois, ça peut être
utile que l'exception se propage à travers le join, pour se
rétrouver dans le processus qui a démarrer le thread.



ok; en cas de terminaison anormale par exception (catchée et
traitée dans le thread secondaire), j'ai tendance à retourner
une copie de l'exception (ou un code d'erreur) par le même
mécanisme que celui qui poste les résultats (en gros envoi
d'une struct avec a) code d'execution (OK / KO) b) résultat).

Sylvain.
Avatar
James Kanze
On Sep 14, 6:12 pm, Sylvain SF wrote:
James Kanze wrote on 14/09/2008 11:54:
> il faut que la fonction dont on passe l'adresse ait un
> linkage "C". Et une fonction membre, même static, ne peut
> pas avoir un linkage "C".



oui. avec un compilo MS on utilise un modifier propriétaire
pour indiquer cela, avec GCC (ou autre) et les fonctions
POSIX, je n'ai aucune idée du moyen de faire cela, j'ai
supposé que cela existait.



En fin de compte, évidemment, c'est le système qui impose les
règles en ce qui concerne ses fonctions. Mais si le système
définit son API en C, comme fait Posix, et ne spécifie rien de
plus, il faut que tous les pointeurs à des fonctions aient un
linkage "C". Ça m'étonnerait un peu, d'ailleurs, que Windows
n'exigent pas réelement un linkage "C" (mais je n'ai jamais eu
l'occasion de l'essayer).

Ensuite, il y a la question des erreurs dans le compilateur. G++
ne vérifie pas le linkage. C'est une erreur, et ça fait que du
code illégal se compile. D'autres compilateurs, pour les mêmes
plateformes (Solaris, par exemple), génère bien la diagnostique
exigée par la norme.

[...]
>>> ignorer les exceptions



> Ce n'est pas ça qui m'intéressait. Je me posais la question
> sur leur transmission vers le thread appelant. Parfois, ça
> peut être utile que l'exception se propage à travers le
> join, pour se rétrouver dans le processus qui a démarrer le
> thread.



ok; en cas de terminaison anormale par exception (catchée et
traitée dans le thread secondaire), j'ai tendance à retourner
une copie de l'exception (ou un code d'erreur) par le même
mécanisme que celui qui poste les résultats (en gros envoi
d'une struct avec a) code d'execution (OK / KO) b) résultat).



Vue qu'on n'a pas d'autres voies de communication:-). Il faut
bien que l'exception se propage par une des voies que j'avais
décris pour communiquer les résultats. Mais selon ce qu'on fait,
join pourrait convenir très bien. Il est aussi raisonable dans
certains cas de choisir une voie différente pour les exceptions,
mettant que les threads travaillent directement sur des données
partagées (par exemple, dans la parallelisation d'un traitement
sur un tableau, où chaque thread traite une partie disjointe du
tableeau), mais que les exceptions propagent par le join.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Sylvain SF
James Kanze wrote on 15/09/2008 02:27:

Ça m'étonnerait un peu, d'ailleurs, que Windows n'exigent
pas réelement un linkage "C".



je voulais dire que c'est bien le cas, mais tu me fais douter.

si linkage "à la C" ou "à la C++" signifie décoration (mangling)
des noms de fonctions, alors oui on doit utiliser un "__stdcall"
(qui est utilisé par toutes les API C de la toolbox Win32).

si ce "mode de linkage" désigne autre chose, je n'ai aucune idée
des spécificités du linkeur MS.

Sylvain.
Avatar
Sylvain SF
Sylvain SF wrote on 15/09/2008 03:35:

je voulais dire que c'est bien le cas, mais tu me fais douter.



et dire des anneries.

la convention d'appel est imposé comme "nettoyage par la fonction"
mais la méthode statique passé en paramètre à "CreateThread"
(création d'un thread selon MS) n'est pas déclarée 'extern "C"'.

je n'avais jamais fait attention à ce "petit détail".

Sylvain.
Avatar
Michael DOUBEZ
Sylvain SF a écrit :
Sylvain SF wrote on 15/09/2008 03:35:

je voulais dire que c'est bien le cas, mais tu me fais douter.



et dire des anneries.

la convention d'appel est imposé comme "nettoyage par la fonction"
mais la méthode statique passé en paramètre à "CreateThread"
(création d'un thread selon MS) n'est pas déclarée 'extern "C"'.

je n'avais jamais fait attention à ce "petit détail".



Je ne comprends toujours pas le problème. Si l'ABI est la même alors les
convention d'appel sont respectées et, à moins que l'exception ne se
propage jusqu'à la couche POSIX, il n'y a pas de problème.

--
Michael
Avatar
James Kanze
On Sep 15, 3:35 am, Sylvain SF wrote:
James Kanze wrote on 15/09/2008 02:27:
> Ça m'étonnerait un peu, d'ailleurs, que Windows n'exigent
> pas réelement un linkage "C".



je voulais dire que c'est bien le cas, mais tu me fais douter.



si linkage "à la C" ou "à la C++" signifie décoration
(mangling) des noms de fonctions, alors oui on doit utiliser
un "__stdcall" (qui est utilisé par toutes les API C de la
toolbox Win32).



Non seulement la décoration, mais les conventions de l'appel.
Par exemple, si la pile est nettoyé par l'appelant ou par
l'appelé, l'ordre que les arguments sont pushés sur la pile,
éventuellement si certains arguments sont passés par des
régistres ou non. Dans la pratique, je sais qu'il y a des
différences dans le premier selon les conventions de linkage
chez Microsoft. Mais je ne sais pas quelle convention fait quoi.

Aussi, dans la mesure qu'il s'agit d'une extension propre à
Microsoft, il se peut aussi que le compilateur s'arrange des
choses derrière le dos, que quand tu as un pointeur à une
fonction avec des conventions habituelles C++, et que tu
l'affectes à un pointeur qui utilise d'autres conventions, que
le compilateur génère une trampoline, pour adjuster l'appel, et
qu'il passe l'adresse de cette trampoline.

si ce "mode de linkage" désigne autre chose, je n'ai aucune
idée des spécificités du linkeur MS.



Ce n'est pas tellement l'éditeur de liens. C'est d'une part la
génération de l'appel : le passage des paramètres et la valeur
de rétour, etc.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On Sep 15, 9:39 am, Michael DOUBEZ wrote:
Sylvain SF a écrit :



> Sylvain SF wrote on 15/09/2008 03:35:



>> je voulais dire que c'est bien le cas, mais tu me fais
>> douter.



> et dire des anneries.



> la convention d'appel est imposé comme "nettoyage par la
> fonction" mais la méthode statique passé en paramètre à
> "CreateThread" (création d'un thread selon MS) n'est pas
> déclarée 'extern "C"'.



> je n'avais jamais fait attention à ce "petit détail".



Je ne comprends toujours pas le problème. Si l'ABI est la
même[...]



C'est là précisement la question. Du point du vue de la norme,
c'est le linkage qui détermine l'ABI ; le C et le C++ ne sont
pas obligé à avoir le même linkage (et en partie, ne peut
l'avoir, puisque l'ABI du C++ précise aussi l'organisation de la
vtable, etc.). De même, Microsoft offre plusieurs linkage
différents (avec des ABI différentes) en guise d'extension.

alors les convention d'appel sont respectées et, à moins que
l'exception ne se propage jusqu'à la couche POSIX, il n'y a
pas de problème.



Mais les conventions d'appel dépendent du linkage. Je me suis
bien servi des compilateurs où les conventions étaient
différentes entre le C et le C++. Si on ne peut guère concevoir
du cas sur une machine RISC type Sparc, il y a de fortes raisons
pour supporter de différentes conventions sur l'architecture
Intel.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Michael DOUBEZ
James Kanze a écrit :
On Sep 15, 9:39 am, Michael DOUBEZ wrote:
Sylvain SF a écrit :



Sylvain SF wrote on 15/09/2008 03:35:
la convention d'appel est imposé comme "nettoyage par la
fonction" mais la méthode statique passé en paramètre à
"CreateThread" (création d'un thread selon MS) n'est pas
déclarée 'extern "C"'.


Je ne comprends toujours pas le problème. Si l'ABI est la
même[...]



C'est là précisement la question. Du point du vue de la norme,
c'est le linkage qui détermine l'ABI ; le C et le C++ ne sont
pas obligé à avoir le même linkage (et en partie, ne peut
l'avoir, puisque l'ABI du C++ précise aussi l'organisation de la
vtable, etc.). De même, Microsoft offre plusieurs linkage
différents (avec des ABI différentes) en guise d'extension.



Dans ce cas, comment appeler du C++ à partir du C ? Si je me souviens
bien, il n'y a pas de moyen portable de faire cela (pas de extern "c++"
dans la norme C).

A moins d'une particularité de compilateur (un pragma?), il faut dont
bien à un moment que les ABI communiquent de C vers C++ sinon la norme
POSIX ne sers plus à rien.

alors les convention d'appel sont respectées et, à moins que
l'exception ne se propage jusqu'à la couche POSIX, il n'y a
pas de problème.



Mais les conventions d'appel dépendent du linkage. Je me suis
bien servi des compilateurs où les conventions étaient
différentes entre le C et le C++.



Dans ce là, comment est ce que ça se passe? Est ce que certaines
fonctions sont exportées avec une ABI différente ou est ce qu'il faut
spécifier l'ABI de la fonction à utiliser ?

Si on ne peut guère concevoir
du cas sur une machine RISC type Sparc, il y a de fortes raisons
pour supporter de différentes conventions sur l'architecture
Intel.



--
Michael
1 2 3