10
votes

Je dois mettre en œuvre un moyen de dormir ce fil jusqu'à ce qu'il fonctionne à faire

D'accord, cette question ne concerne donc pas exactement la gestion de thread ... Eh bien, en quelque sorte. Je cherche différentes solutions à cette configuration. J'ai quelques idées, mais je cherche des solutions pouvant satisfaire le problème. Et pesera les avantages et les inconvénients pour mettre en œuvre le meilleur.

Voici la situation.

J'ai une application de gestionnaire qui apparaîtra un threads. Ce fil fonctionnera et gérera en permanence la communication série avec des cartes connectées au système via USB. L'application Manager facilite la communication entre d'autres applications exécutées sur le système et ce fil. Le fil doit vraiment effectuer deux choses:

  1. sondez les cartes pour des échantillons de données via une minuterie variable .. Habituellement environ une fois par minute (le bus série est plutôt lent, Baud est 4800. Je ne peux pas contrôler cela)
  2. faciliter la communication avec l'application Manager. (Oui. D'autres applications demanderont des données d'échantillonnage, le gestionnaire transfère la demande au thread. Le thread exécute l'opération et renvoie les données)

    Mon design initial était simple et fonctionne. J'utilise une file d'attente et un mutex pour responsable de la communication. Donc, la logique du fil est la suivante:

    1. initialisation
    2. Bien que nous n'avions pas reçu de commande d'arrêt du gestionnaire
    3. Si notre minuterie est en place, sondez la carte pour les données
    4. Sinon, vérifiez si nous avons un message posté par le gestionnaire de la file d'attente. Si oui, traitez-le

      Le problème est que je n'ai pas considéré l'utilisation du processeur. 99,9% du temps où mon thread ne traite rien et ne vient de sucer le pouvoir. Je dois mettre en œuvre un moyen de dormir ce fil jusqu'à ce qu'il ait du travail à faire. Donc quelques idées:

      Utilisez SELECT () à bloquer. Cela peut bloquer en fonction de la minuterie que j'ai besoin d'utiliser et je pourrais modifier la mise en œuvre de la messagerie en file d'attente en prise. Messagerie. Donc, à la place, le fil ouvrirait une prise client à la gestionnaire et le gestionnaire transmettraient les messages sur la prise à la fil. Puis sélectionnez () dormirait jusqu'à ce qu'il y ait une activité sur la FD ou ma minuterie était en place.

      pro: exactement la fonctionnalité dont j'ai besoin.

      CON: ne pas être des sockets un peu lourd pour la communication à un thread où vous partagez déjà la mémoire?

      Utilisez un système de signal. (Quelqu'un de plus bien informé à Linux peut pipe ici avec un exemple de mise en œuvre ... Je ne suis pas sûr de savoir exactement comment faites-le.) Mais le fil pourrait dormir pendant la durée de la minuterie, et Réveillez-vous pour traiter si un signal a été reçu du gestionnaire.

      pro: maintient la mise en œuvre actuelle à l'aide de la mémoire partagée

      Con: Je ne sais pas comment mettre en œuvre. Y a-t-il une fonction comme SELECT () qui fonctionne avec des signaux au lieu de FDS?

      potentiellement un mutex. Je pourrais bloquer jusqu'à ce qu'un mutex soit affecté par le gestionnaire.

      Pro: partage encore la mémoire

      Con: Peut-être besoin de déplacer le traitement du minuteur au gestionnaire et qui n'est vraiment pas une option car il a d'autres minuteries et un travail critique à effectuer.

      S'il vous plaît recommander et n'hésitez pas à critiquer. Je suis ouvert à toutes les options efficaces. Veuillez noter que cela s'exécute sur un système intégré, l'utilisation des ressources est donc essentielle.


5 Réponses :


3
votes

Demandez à chaque fil d'attente sur une file d'attente de producteur d'intrants avec un délai d'attente. Si les temps d'attente de la file d'attente, sondez la liaison série, sinon traiter la commande reçue sur la file d'attente. Pour former une file d'attente appropriée à partir de zéro, vous avez besoin d'une file d'attente (que vous avez déjà), un mutex pour protéger les pointeurs / index de la file d'attente (que vous avez déjà) et un sémaphore, initialisé à 0, avec une attente ( délai d'attente) fonction. Pour envoyer une demande du fil, verrouillez le mutex, appuyez sur la requête, déverrouillez le mutex, signaler le sémaphore. Dans le fil, attendez sur le sémaphore, si vous attendez sans délai, verrouillez le mutex, POP La demande (car il y en aura toujours un), déverrouillez le mutex et traiter la demande reçue. Si la SEMA WAIT revient avec un délai d'attente, sondez la liaison série. Lorsque fait, boucle pour attendre à nouveau sur le sémaphore.

Pour varier le délai d'attente, envoyez un message sur le thread avec la commande 'echangewaitInterval', (disons :) et le nouvel intervalle de délai d'attente à utiliser pour les attentes ultérieures.


0 commentaires

4
votes

Basculez sur les files d'attente de message POSIX au lieu de votre propre. mq_timédreceive reviendra si le gestionnaire publie une demande. Si cela tente, vous devez faire votre sondage de minuterie. La synchronisation et le blocage sont déjà emballés.


1 commentaires

Un peu trop lourd, les sémaphores sont un peu plus simples.



5
votes

L'outil classique pour gérer de telles situations sont des sémaphores et non des mutiles ou des variables de condition. Pensez à eux comme des jetons passés du manager au fil.

Le thread pourrait utiliser SEM_TIMEDWAIT pour vous assurer de vous réveiller de temps en temps pour vérifier les données.

méfiez-vous pour capturer les retours d'erreur de SEM _ fonctionne bien, ils sont interruptibles. Donc, vous pouvez avoir un peu plus de réveil que vous ne le pensez.


8 commentaires

Un couple de sémaphore répond ici, alors répondra à eux tous ici. Utiliserait un sémaphore comme une minuterie pour se réveiller et vérifier que les données soient plus efficaces que de bloquer avec sélectionner () et recevoir les données sur une prise? D'une part, j'utilise une mémoire partagée au lieu de RECV (), mais de l'autre, si je ne reçois jamais de demande d'une autre application. Je traite sans raison.


Comme vous décrivez votre problème, le fait que votre fil ne dort pas est vraiment significatif. Cela signifie que si vous le tournez, votre fil ne fonctionne pas beaucoup de travail par rapport à cela. Donc, si vous maîtrisez ce problème "sommeil", quelle solution que vous prenez aura beaucoup d'impact sur le travail global (cycles de processeur) utilisés par le fil. Les différences sont alors dans (1) commodité, ce qui est programmé et maintenu la latence la plus facile (2). Pour les deux, je pense que les sémaphores gagnent.


Les attentes du sémaphore bloquent. Si l'attente () n'est pas encore expirée et que le sémaphore n'est pas encore signalé et son décompte interne est zéro, aucun processeur n'est utilisé du tout. L'attente SEM (Timeout) ne reviendra pas avant que d'une condition ou de l'autre soit satisfaite. Si les temps d'attente sont sortis, la cuve sera courante une fois. Si le sémaphore est signalé 15 fois par les messages de file d'attente du gestionnaire, le thread fonctionnera ensuite 15 fois et traitera tous les 15 et pas plus.


OH - Notez bien le commentaire de Jens sur les réveilles d'interruption. Sur le système d'exploitation défectueux qui supportent cette "fonctionnalité de réveil parasite", vous devez vérifier :(


@Martinjames, «parasites WakeUps» ne sont pas une faute mais prévu en tant que telle. Cela permet d'écrire un code de bas niveau qui réagit sur les interruptions.


Les pilotes Windows @jensGustedt sont des indicateurs de bas niveau qui réagissent sur des interruptions, des sémaphores / événements Windows / etc. permettent la communication avec ce code et entre les threads. Différence - il n'y a jamais de réveil parasite, jamais. Nous devrons peut-être accepter de différer à ce sujet, mais je considère que tout réveil faux faux sur un signal qui n'a pas été signalé comme un bogue grave qui, bien qu'il y ait une solution de contournement de l'espace utilisateur, aurait dû être traitée dans le noyau où tout De telles «fonctionnalités» devraient rester. Dijkstra se retournerait dans sa tombe ..


@Martinjames, cette question est clairement étiquetée sous la forme de Linux, c'est-à-dire un système POSIX. Je ne sais pas grand chose à propos de Windows, et en particulier à cet égard aux sémaphores, et j'espère que jamais ne sera jamais tenté de faire face à un système d'exploitation que je ne connais pas grand chose. Dans POSIX SEM_T est conçu exprès pour être utilisable dans les gestionnaires de signaux, il s'agit d'un outil de faible niveau par conception. Cela n'a rien à voir avec le système d'exploitation "défectueux" et certainement rien à voir avec Dijstra. Dans tout ce que je considère votre remarque assez inappropriée.


Je prends ton point et je ne voulais pas être offensant ou inapproprié. OTOH, Windows, RTOS, FAXEROS, RMX, CTL, OS2, ETC ET. Vous n'avez pas de réveil parasite. Seules les varants U ** x semblent avoir cette fonctionnalité.



0
votes

L'approche classique Pthreads de ce type serait d'avoir votre bloc de fil dans pthread_cond_wait () jusqu'à ce que le thread du gestionnaire met un message dans la file d'attente et signale la variable de condition. Dans ce cas, se réveiller de manière opportune pour interroger les dispositifs de série, utilisez pthread_cond_timpedwait () à la place.


0 commentaires

4
votes

Essayez quelque chose comme ceci, à l'aide de sémaphores: xxx

Vous pouvez remplacer sem_wait avec SEM_TIMEDWAIT Si vous avez besoin d'un délai d'attente.


0 commentaires