Question

Existe-t-il des inconvénients à appeler pthread_cond_timedwait sans verrouiller d’abord le mutex associé et à ne pas prendre de verrou mutex lors de l’appel de pthread_cond_signal?

Dans mon cas, il n’existe vraiment aucune condition à vérifier. Je souhaite un comportement très similaire à Java (wait (long) et notify ().

Selon la documentation, il peut exister un "comportement de planification imprévisible". Je ne sais pas ce que cela signifie.

Un exemple de programme semble fonctionner correctement sans verrouiller d'abord les mutex.

Était-ce utile?

La solution

Le premier n'est pas OK:

  

Le pthread_cond_timedwait () et   Les fonctions pthread_cond_wait () doivent   bloquer sur une variable de condition. Ils   doit être appelé avec mutex verrouillé par   le thread appelant ou indéfini   résultats de comportement.

http://opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html / a>

La raison en est que l’implémentation peut vouloir que le mutex soit verrouillé afin de vous ajouter en toute sécurité à une liste de serveurs. Et il se peut qu’il veuille libérer le mutex sans vérifier au préalable qu’il est maintenu.

La seconde est dérangeante:

  

si le comportement de planification prévisible est   nécessaire, alors ce mutex est verrouillé par   le fil appelant    pthread_cond_signal () ou    pthread_cond_broadcast () .

http://www.opengroup.org/onlinepubs/007908775/xsh/ pthread_cond_signal.html

De mémoire, je ne sais pas quelle est la condition de concurrence spécifique qui gêne le comportement du planificateur si vous signalez sans prendre le verrou. Donc, je ne sais pas à quel point le comportement d’un planificateur indéfini peut s’aggraver: par exemple, avec la diffusion, les serveurs n’ont tout simplement pas le verrou dans l’ordre de priorité (ou quel que soit le comportement normal de votre planificateur). Ou peut-être que les serveurs peuvent être "perdus".

En règle générale, avec une variable de condition, vous souhaitez définir la condition (au moins un drapeau) et le signal, plutôt que simplement le signal, et pour cela, vous devez utiliser le mutex. La raison en est que sinon, si vous êtes en concurrence avec un autre thread appelant wait (), vous obtenez un comportement complètement différent selon que wait () ou signal () gagne: si le signal () se faufile en premier, alors vous attendez le délai d’attente complet même si le signal qui vous tient à coeur est déjà passé. C'est rarement ce que les utilisateurs de variables de condition veulent, mais cela peut vous convenir. C’est peut-être ce que les docs entendent par "comportement imprévisible du planificateur". - soudainement, le laps de temps devient critique pour le comportement de votre programme.

Btw, en Java, vous devez avoir le verrou pour notifier () ou notifyAll ():

  

Cette méthode ne doit être appelée que par un   fil qui est le propriétaire de cette   le moniteur de l'objet.

http: // java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#notify ()

Le comportement Java synchronisé {/} / wait / notifty / notifyAll est analogue à pthread_mutex_lock / pthread_mutex_unlock / pthread_cond_wait / pthread_cond_signal / pthread_cond_broadcast, et non par coïncidence.

Autres conseils

L'excellent "Programmation avec des threads POSIX" de Butenhof discute de cela directement à la fin du chapitre 3.3.3.

En gros, signaler le condvar sans verrouiller le mutex est une optimisation des performances potentielle : si le thread de signalisation a le mutex verrouillé, le thread qui se réveille sur le condvar doit immédiatement bloquer sur le mutex que le thread de signalisation est verrouillé même si le thread de signalisation ne modifie aucune des données que le thread en attente utilisera.

Raison pour laquelle le "comportement imprévisible du planificateur" Il est mentionné que si vous avez un thread hautement prioritaire en attente sur le condvar (lequel un autre thread va signaler et réveiller le thread hautement prioritaire), tout autre thread moins prioritaire peut venir verrouiller le mutex de sorte que signalé et le thread de priorité élevée est réveillé, il doit attendre le thread de priorité inférieure pour libérer le mutex. Si le mutex est verrouillé pendant la signalisation, le thread de priorité supérieure sera programmé sur le mutex avant le thread de priorité inférieure: vous savez en gros que, lorsque vous vous "réveillez", le thread hautement prioritaire s’éveillera dès que le planificateur le permettra (bien sûr, vous devrez peut-être attendre le mutex avant de signaler le thread hautement prioritaire, mais c’est un problème différent).

Le point d’attente de la variable conditionnelle associée à un mutex est de atomiquement entrer wait et de libérer le verrou, c.-à-d. permettre aux autres threads de modifier l’état de protection, puis de nouveau recevoir de manière atomique la notification du changement d’état. et acquérir la serrure. Ce que vous décrivez peut être réalisé avec de nombreuses autres méthodes telles que les tuyaux, les sockets, les signaux ou - probablement la plus appropriée - sémaphores .

Je pense que cela devrait fonctionner (notez le code non testé):

// initialize a semaphore
sem_t sem;
sem_init(&sem,
    0, // not shared
    0  // initial value of 0
    );


// thread A
struct timespec tm;
struct timeb    tp;

const long sec      = msecs / 1000;
const long millisec = msecs % 1000;

ftime(&tp);
tp.time += sec;
tp.millitm += millisec;
if(tp.millitm > 999) {
    tp.millitm -= 1000;
    tp.time++;
}
tm.tv_sec  = tp.time;
tm.tv_nsec = tp.millitm * 1000000;

// wait until timeout or woken up
errno = 0;
while((sem_timedwait(&sem, &tm)) == -1 && errno == EINTR) {
    continue;
}

return errno == ETIMEDOUT; // returns true if a timeout occured


// thread B
sem_post(&sem); // wake up Thread A early

Les conditions doivent être signalées à l'extérieur du mutex dans la mesure du possible. Les mutex sont un mal nécessaire dans la programmation concurrente. Leur utilisation entraîne des conflits qui privent le système des performances maximales qu’il peut tirer de l’utilisation de plusieurs processeurs.

Le but d'un mutex est de protéger l'accès à certaines variables partagées du programme afin qu'elles se comportent de manière atomique. Lorsqu'une opération de signalisation est effectuée à l'intérieur d'un mutex, des centaines de cycles de machine non pertinents y sont inclus, ce qui n'a rien à voir avec la protection des données partagées. Potentiellement, il appelle depuis un espace utilisateur jusqu'au noyau.

Remarques sur le "comportement prévisible du planificateur" dans la norme sont complètement faux.

Lorsque nous voulons que la machine exécute des instructions dans un ordre prévisible et bien défini, l'outil utilisé est le séquencement des instructions dans un seul thread d'exécution: S1; S2 . La déclaration S1 est "planifiée". avant S2 .

Nous utilisons des threads lorsque nous réalisons que certaines actions sont indépendantes et que leur ordre de planification n'est pas important et que des avantages en termes de performances doivent être réalisés, tels qu'une réponse plus rapide aux événements en temps réel ou un traitement sur plusieurs processeurs.

Parfois, lorsque des ordres de planification deviennent importants entre plusieurs threads, cela relève d'un concept appelé priorité . La priorité résout ce qui se produit en premier lorsque l'une des N instructions peut éventuellement être programmée pour être exécutée La mise en file d'attente est un autre outil permettant de passer des commandes en multithreading. Les événements sont placés dans une file d'attente par un ou plusieurs threads et un seul thread de service traite les événements dans l'ordre de la file d'attente.

En bout de ligne, l'emplacement de pthread_cond_broadcast n'est pas un outil approprié pour contrôler l'ordre d'exécution. Cela ne rendra pas l'ordre d'exécution prévisible en ce sens que le programme a soudainement le même comportement reproductible sur toutes les plateformes.

"comportement de planification imprévisible" signifie juste que. Vous ne savez pas ce qui va se passer. Ni faire la mise en œuvre. Cela pourrait fonctionner comme prévu. Cela pourrait bloquer votre application. Cela pourrait fonctionner pendant des années, puis une situation de concurrence met en marche votre application. Il pourrait y avoir une impasse.

En gros, si une documentation suggère que quelque chose d'indéfini ou d'imprévisible puisse se produire à moins que vous ne fassiez ce que la documentation vous dit de faire, vous feriez mieux de le faire. D'autres choses pourraient exploser dans votre visage. (Et ça ne va pas exploser tant que vous n'avez pas mis le code en production, histoire de vous déranger encore plus. Au moins, c'est mon expérience)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top