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

Thread et condition (pas taper)

16 réponses
Avatar
Bruno CAUSSE
Bonsoir,

Je sais que le groupe n'est pas le plus adapté (et encore), mais vous etes
"sympas" compétents et francophones.

Je me sers d'une condition pour faire une "sorte" de sémaphore.

class RXEngine {
.../...
POSIXThread condition;
.../...

};

appelé par le thread "ordonnanceur" pour (re)donner la priorité au thread
"traitement"

//sans commentaire
void RXEngine::is_priority() {

mutex_priority.lock();
bool _priority = priority;
mutex_priority.unlock();

return _priority;
}

appelé par le thread "ordonnanceur" pour donner la priorité au thread
"traitement"

void RXEngine::set_priority() {

mutex_priority.lock();
if(!priority) {
priority = true;
condition.signal();
}
mutex_priority.unlock();
}

appelé par le thread "ordonnanceur" pour retirer la priorité au thread
"traitement"

void RXEngine::wait() {

mutex.lock();

mutex_priority.lock();
priority = false;
mutex_priority.unlock();

mutex.unlock();

}

Pendant le traitement ce code est évalué en "boucle"


.../...
if(!isPriority()) {
timer.wait();
condition.wait();
timer.restart();
}
.../...

Ce code me donne satisfaction (enfin presque sinon il n'y aurai pas de
question)

A la première utilisation mon thread traitement ne s'arrête pas, il faut
attendre le deuxième passage. Apres ça baigne.

Que se passe t'il?

Comment régler cela?

10 réponses

1 2
Avatar
kanze
Bruno CAUSSE wrote:

Je sais que le groupe n'est pas le plus adapté (et encore),
mais vous etes "sympas" compétents et francophones.


Et il n'y a pas de groupe Posix francophone où les gens sont
aussi sympas et compétents ?

Je me sers d'une condition pour faire une "sorte" de sémaphore.

class RXEngine {
.../...
POSIXThread condition;


C'est quoi, POSIXThread. Est-ce qu'il s'agit ici d'un wrapper
pour un pthread_cond_t, ou... ?

.../...

};

appelé par le thread "ordonnanceur" pour (re)donner la priorité au th read
"traitement"

//sans commentaire
void RXEngine::is_priority() {

mutex_priority.lock();
bool _priority = priority;
mutex_priority.unlock();

return _priority;
}

appelé par le thread "ordonnanceur" pour donner la priorité au thread
"traitement"

void RXEngine::set_priority() {

mutex_priority.lock();
if(!priority) {
priority = true;
condition.signal();
}
mutex_priority.unlock();
}

appelé par le thread "ordonnanceur" pour retirer la priorité
au thread "traitement"

void RXEngine::wait() {

mutex.lock();

mutex_priority.lock();
priority = false;
mutex_priority.unlock();

mutex.unlock();
}

Pendant le traitement ce code est évalué en "boucle"

.../...
if(!isPriority()) {
timer.wait();
condition.wait();
timer.restart();
}
.../...


Si condition est un pthread_cond_t et mutex_priority est un
pthread_mutex_t, et les fonctions membre correspondent aux
requêtes Posix des mêmes noms, c'est incorrect. On doit être en
possession du mutex lors de l'appel de pthread_cond_wait. Donc,
on gros, ce qu'il faut ici, c'est :

mutex_priority.lock() ;
if ( ! priority ) {
condition.wait() ;
}
mutex_priority.unlock() ;

Appeler pthread_cond_wait sans avoir contrôle du mutex donne un
comportement indéfini, selon la norme Posix.

Ce code me donne satisfaction (enfin presque sinon il n'y
aurai pas de question)

A la première utilisation mon thread traitement ne s'arrête
pas, il faut attendre le deuxième passage. Apres ça baigne.

Que se passe t'il?

Comment régler cela?


Deux choses à vérifier. D'abord, que le pthread_cond_t est bien
initialisé AVANT le premier appel à pthread_cond_wait, et
deuxièmement, que tu detiens toujours le mutex chaque fois que
tu appelles pthread_cond_wait.

--
James Kanze GABI Software
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
Bruno CAUSSE
dans l'article , kanze
à a écrit le 21/03/06 10:34 :

Deux choses à vérifier. D'abord, que le pthread_cond_t est bien
initialisé AVANT le premier appel à pthread_cond_wait, et
deuxièmement, que tu detiens toujours le mutex chaque fois que
tu appelles pthread_cond_wait.


Merci pour ta réponse :-)

Effectivement après plusieurs lectures j'ai entouré :

condition.lock_mutex();
condition.signal();
condition.unlock_mutex();

Et

condition.lock_mutex();
condition.wait();
condition.unlock_mutex();

C'est quoi, POSIXThread. Est-ce qu'il s'agit ici d'un wrapper
pour un pthread_cond_t, ou... ?


Oui, j'utilise ce truc très simple trouvé sur le net :

http://www.partow.net/programming/posixsynchwrapper/index.html

Plutôt que Boost ou Ace qui m'effraies encore.

Et il n'y a pas de groupe Posix francophone où les gens sont
aussi sympas et compétents ?


Oui... Mais ou ????

Appeler pthread_cond_wait sans avoir contrôle du mutex donne un
comportement indéfini, selon la norme Posix.


C'est énervent un comportement indéfini qui fonctionne bien :-)


Merci bcq
--
Bruno

Avatar
kanze
Bruno CAUSSE wrote:
dans l'article
, kanze à
a écrit le 21/03/06 10:34 :

Deux choses à vérifier. D'abord, que le pthread_cond_t est
bien initialisé AVANT le premier appel à pthread_cond_wait,
et deuxièmement, que tu detiens toujours le mutex chaque
fois que tu appelles pthread_cond_wait.


Effectivement après plusieurs lectures j'ai entouré :

condition.lock_mutex();
condition.signal();
condition.unlock_mutex();

Et

condition.lock_mutex();
condition.wait();
condition.unlock_mutex();


Attention ! Normalement, il faut tester la variable de contrôle
et entrer dans wait sans lacher le mutex entre temps. Sinon, tu
as une possibilité d'une race (« race condition », je ne suis
pas sûr de la traduction française correcte) :

Thread A Thread B

teste variable,
le trouve fausse...

prend le main, met la
variable à vrai, puis fait
signal()

entre dans le wait...

Pour thread B, thread A a été activé. Thread A, en revanche,
attend toujours l'activation. (Dans beaucoup de cas, thread B
finira par attendre une réponse de thread A, pour savoir si la
requête s'est bien passée. Après le scénario ci-dessus, il
risque d'attendre longtemps.) Il ne faut pas que le thread A
puisse être interrompu entre le test de la variable et l'entrée
dans le wait.

Je t'avais indiqué l'idiome consacré. Il ne faut s'en écarter
que dans des cas très spéciax, et s'en écarter comporte de
grandes risques.

À titre indicatif, je t'offre du code de l'implémentation Unix
de ma AbstractMessageQueue:

void
AbstractMessageQueue::send(
void* message )
{
TrivialMutexLocker lock( myImpl->mutex ) ;
myImpl->queue.push_back( message ) ;
pthread_cond_signal( &myImpl->cond ) ;
}

void*
AbstractMessageQueue::poll()
{
void* result = NULL ;
TrivialMutexLocker lock( myImpl->mutex ) ;
if ( ! myImpl->queue.empty() ) {
result = myImpl->queue.front() ;
myImpl->queue.pop_front() ;
}
return result ;
}

void*
AbstractMessageQueue::receive()
{
TrivialMutexLocker lock( myImpl->mutex ) ;
while ( myImpl->queue.empty() ) {
pthread_cond_wait( &myImpl->cond, &myImpl->mutex ) ;
}
void* result = myImpl->queue.front() ;
myImpl->queue.pop_front() ;
return result ;
}

void*
AbstractMessageQueue::receive(
time_t timeout )
{
bool isTimedOut = false ;
void* result = NULL ;
timespec absTimeout ;
clock_gettime( CLOCK_REALTIME, &absTimeout ) ;
absTimeout.tv_sec += timeout ;
TrivialMutexLocker lock( myImpl->mutex ) ;
while ( ! isTimedOut && myImpl->queue.empty() ) {
switch ( pthread_cond_timedwait(
&myImpl->cond, &myImpl->mutex, &absTimeout ) )
{
case 0 :
break ;

case ETIMEDOUT :
isTimedOut = true ;
break ;

default :
assert( 0 ) ;
break ;
}
}
if ( ! myImpl->queue.empty() ) {
result = myImpl->queue.front() ;
myImpl->queue.pop_front() ;
}
return result ;
}

(MessageQueue même est un template, qui dérive de
AbstractMessageQueue. Et AbstractMessageQueue utilise l'idiome
du pare-feu de compilation, de façon à ce que le même en-tête
sert à la fois sous Unix et sous Windows -- il n'y a que
l'implémentation qui change. Le queue même, c'est un
std::deque<void*>.

L'attitude vis-à-vis des erreurs possibles est assez cavalière
aussi. Dans un code « production », il en faudrait bien plus.)

Note surtout les durées que le mutex est tenu.

Et je m'excuse auprès des lecteurs habituels pour ce code, qui
est manifestement spécifique à Posix (mais je suis convaincu
qu'il fonctionne aussi sous Linux).

C'est quoi, POSIXThread. Est-ce qu'il s'agit ici d'un
wrapper pour un pthread_cond_t, ou... ?


Oui, j'utilise ce truc très simple trouvé sur le net :

http://www.partow.net/programming/posixsynchwrapper/index.html

Plutôt que Boost ou Ace qui m'effraies encore.


Ace m'effraie aussi. D'autant plus qu'il n'a pas marché pour ce
qu'il m'a fallu (un socket UDP), et que j'ai trouvé des erreurs
dans la gestion de thread par ailleurs.

Boost entier peut être un peu effrayant aussi, vue la quantité
de ce qu'il comporte. Si Boost::treads a un défaut, en revanche,
c'est qu'il est trop simple.

Et il n'y a pas de groupe Posix francophone où les gens sont
aussi sympas et compétents ?


Oui... Mais ou ????


Il y a bien un groupe fr.comp.os.unix. Chaque fois que je l'ai
fréquenté, j'ai trouvé les gens compétents et sympas. C'est vrai
que la plupart des postings semblent concernaient plutôt le
shell, mais je crois que les questions sur la programmation y
sont acceptables aussi.

Je ne trouve pas d'équivalent francophone à comp.threads. C'est
dommage ; dans les groupes anglophones, c'est bien là où se
trouvent les experts pour ce genre de question.

Appeler pthread_cond_wait sans avoir contrôle du mutex donne
un comportement indéfini, selon la norme Posix.


C'est énervent un comportement indéfini qui fonctionne bien :-)


En règle générale, quand il y a un comportement indéfini, le
code fonctionne parfaitement lors de tes tests, pour se mettre
en erreur d'une façon spectaculaire lors de la démo devant le
client important.

--
James Kanze GABI Software
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
Bruno CAUSSE
dans l'article , kanze à
a écrit le 21/03/06 16:24 :

Attention ! Normalement, il faut tester la variable de contrôle
et entrer dans wait sans lacher le mutex entre temps. Sinon, tu
as une possibilité d'une race (« race condition », je ne suis
pas sûr de la traduction française correcte) :

Thread A Thread B

teste variable,
le trouve fausse...

prend le main, met la
variable à vrai, puis fait
signal()

entre dans le wait...

Pour thread B, thread A a été activé. Thread A, en revanche,
attend toujours l'activation. (Dans beaucoup de cas, thread B
finira par attendre une réponse de thread A, pour savoir si la
requête s'est bien passée. Après le scénario ci-dessus, il
risque d'attendre longtemps.) Il ne faut pas que le thread A
puisse être interrompu entre le test de la variable et l'entrée
dans le wait.

Je t'avais indiqué l'idiome consacré. Il ne faut s'en écarter
que dans des cas très spéciax, et s'en écarter comporte de
grandes risques.


mutex_priority : pour la synchronisation de la variable priority
mutex pour la synchronisation des methodes start() et wait()


void RXEngine::set_priority() {

mutex_priority.lock();
if(!priority) {
priority = true;

condition.lock_mutex();
condition.signal();
condition.unlock_mutex
}
mutex_priority.unlock();
}

void RXEngine::wait() {

mutex.lock();

mutex_priority.lock();
priority = false;
mutex_priority.unlock();

mutex.unlock();

}

Et dans mon traitement :

mutex_priority.lock();
If(! priority) {

timer.wait();

condition.lock_mutex();
condition.wait();
condition.unlock_mutex();

timer.restart();

}
mutex_priority.unlock();

C'est cela?

Avatar
Bruno CAUSSE
dans l'article C045E102.17874%, Bruno CAUSSE à
a écrit le 21/03/06 16:57 :

C'est cela?


Non

Je bloque mon mutex mutex_priority :(

Avatar
Bruno CAUSSE
mutex_priority : pour la synchronisation de la variable priority
mutex pour la synchronisation des methodes start() et wait()

//sans commentaire
void RXEngine::is_priority() {

mutex_priority.lock();
bool _priority = priority;
mutex_priority.unlock();

return _priority;
}


void RXEngine::set_priority() {

mutex_priority.lock();
if(!priority) {
priority = true;

condition.lock_mutex();
condition.signal();
condition.unlock_mutex
}
mutex_priority.unlock();
}

void RXEngine::wait() {

mutex.lock();

mutex_priority.lock();
priority = false;
mutex_priority.unlock();

mutex.unlock();

}

Et dans mon traitement :

condition.lock_mutex();
If(! is_priority()) {

timer.wait();

condition.wait();

timer.restart();

}
condition.unlock_mutex();

J'espere que je ne vous "gonfle" pas trop avec mon probleme.
Avatar
Bruno CAUSSE
dans l'article C045E549.1787D%, Bruno CAUSSE à
a écrit le 21/03/06 17:15 :

J'espere que je ne vous "gonfle" pas trop avec mon probleme.


et meme supprimer mutex_priority qui fait double emploi avec condition.

Je vais y arriver si si.. :-)

void RXEngine::set_priority() {

condition.lock_mutex();
if(!priority) {
priority = true;

condition.signal();
}
condition.unlock_mutex();
}

void RXEngine::wait() {

mutex.lock();

condition.lock_mutex();
priority = false;
condition.unlock_mutex();

mutex.unlock();

}

Et dans mon traitement :

condition.lock_mutex();
If(! priority) {

timer.wait();

condition.wait();

timer.restart();

}
condition.unlock_mutex();

Avatar
kanze
Bruno CAUSSE wrote:

mutex_priority : pour la synchronisation de la variable priority
mutex pour la synchronisation des methodes start() et wait()


Non ! C'est le même mutex qui doit servir pour les deux.

Sans une régarde dans tes classes, c'est difficile d'être sûr,
mais je crois :

void
RXEngine::set_priority()
{
condition.lock_mutex() ;
if ( ! priority ) {
priority = true ;
condition.signal() ;
}
condition.unlock_mutex() ;
}

void
RXEngine::wait()
{
condition.lock_mutex() ;
while ( ! priority ) {
condition.wait() ;
}
condition.unlock_mutex() ;
}

void RXEngine::set_priority() {

mutex_priority.lock();
if(!priority) {
priority = true;

condition.lock_mutex();
condition.signal();
condition.unlock_mutex
}
mutex_priority.unlock();


Il faut que priority soit modifié sous le mutex de la condition.
Sinon, la modification et le reveil des processus en attente
n'est pas atomique, ce qui mène à la condition de race dont j'ai
parlé.

}

void RXEngine::wait() {

mutex.lock();

mutex_priority.lock();
priority = false;
mutex_priority.unlock();

mutex.unlock();

}


Et ici, je ne vois pas du tout où tu attends la condition.

Et dans mon traitement :

mutex_priority.lock();
If(! priority) {

timer.wait();

condition.lock_mutex();
condition.wait();
condition.unlock_mutex();

timer.restart();

}
mutex_priority.unlock();

C'est cela?


Non. Tu t'obstines à vouloir séparer deux opérations qui ne
peuvent pas être séparées. La gestion de la condition et les
accès à la variable associée sont indissociable. La variable, la
condition et le mutex forment un tout. En pseudo-code :

set()
{
lock mutex ;
modifier variable ;
signal ou broadcast sur la condition.
unlock mutex ;
}

wait()
{
lock mutex ;
while ( ! variable modifiée ) {
wait sur la condition ;
}
si nécessaire, modifier la variable.
unlock mutex ;
}

Note bien que la variable fait aussi partie du triplet. On ne
l'utilise que pour ça, et on ne s'amuse pas à la modifier
autrement.

Dans le code que j'ai posté, la « variable » était un
std::deque, et les modifications en étaient des appels à
push_back et pop_front -- le test était is ! empty. Si tout ce
que tu veux, c'est une simple sémaphore binaire, un bool suffit,
avec = true et = false comme modifications. (Et si c'est une
simple sémaphore binaire, j'appellerais les fonctions get() et
free().)

En plus, je ne comprends pas la rôle du timer. Si le but est de
mettre un time-out, il existe un appel pthread_cond_timedwait
qui fait l'affaire.

Aussi : si les classes de wrapper dont tu te sers n'offre pas la
possibilité de faire un scoped_lock, ou quelque chose du genre,
jette-les. Sinon, le moindre truc qui foire, et tu risques de
bloquer toute l'application.

--
James Kanze GABI Software
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
Bruno CAUSSE
dans l'article , kanze
à a écrit le 21/03/06 17:43 :

}

void RXEngine::wait() {

mutex.lock();

mutex_priority.lock();
priority = false;
mutex_priority.unlock();

mutex.unlock();

}


Et ici, je ne vois pas du tout où tu attends la condition.


Je demande au moteur de s'arreter au prochain passage sur la condition.

Non. Tu t'obstines à vouloir séparer deux opérations qui ne
peuvent pas être séparées. La gestion de la condition et les
accès à la variable associée sont indissociable. La variable, la
condition et le mutex forment un tout. En pseudo-code :

Note bien que la variable fait aussi partie du triplet. On ne
l'utilise que pour ça, et on ne s'amuse pas à la modifier
autrement.


je viens de le comprendre je crois.

En plus, je ne comprends pas la rôle du timer. Si le but est de
mettre un time-out, il existe un appel pthread_cond_timedwait
qui fait l'affaire.


Je programme un jeu de réflexion (Othello). Mon prog joue, sur un serveur de
jeu, des parties simultanées (2 parties identiques en même temps avec les
couleurs inversées) et synchronisées (les coups sont joués en même temps)

Mon but : après avoir terminé la recherche d'un coup, si l'adversaire n'a
pas encore donné le sien (le prog anticipe le coup suivant et réfléchi sur
le temps adverse). Mon ordinateur étant mono core je ne peux mené qu'une
recherche a la fois. Le prog possede deux moteurs, un par jeu. Les moteurs
fonctionnent donc alternativement. D'ou mes interruptions (wait) de
recherche (moteur A/B) pour lancer (un nouvelle recherche si l'anticipation
est mauvaise) ou poursuivre (si l'anticipation est bonne) une recherche sur
l'autre jeu (moteur B/A).

L'ordonnanceur donne la priorité a un moteur en fonction de la demande du
serveur.

Le timer gère le temps (comme sur une partie d'échec)


Aussi : si les classes de wrapper dont tu te sers n'offre pas la
possibilité de faire un scoped_lock, ou quelque chose du genre,
jette-les. Sinon, le moindre truc qui foire, et tu risques de
bloquer toute l'application.


Je travaille avec les thread posix depuis une ou deux semaine seulement.

Encore une fois merci.


Avatar
twxs
kanze wrote:
...

Plutôt que Boost ou Ace qui m'effraies encore.


Ace m'effraie aussi. D'autant plus qu'il n'a pas marché pour ce
qu'il m'a fallu (un socket UDP), et que j'ai trouvé des erreurs
dans la gestion de thread par ailleurs.



bonjour,
Dans notre projet, nous avons migré notre bus CORBA vers ACE/TAO
et pour uniformiser le code la gestion des threads de tout nos modules
est également passée à ACE.
Pour l'instant nous n'avons pas rencontré de problème "apparent"
(attendons la prochaine demo devant le client ;) ) mais j'aimerai
connaitre quelles sont les erreurs de gestions auquel vous faites référence.

Nicolas


1 2