2010-05-24 23 views
0

J'ai une application multi-thread qui utilise pthreads. J'ai un verrou mutex() et des variables de condition(). Il y a deux threads, un thread produisant des données pour le second thread, un worker, qui essaie de traiter les données produites en temps réel de telle sorte qu'un mandrin soit traité aussi près que possible d'une période de temps fixe. Cela fonctionne plutôt bien, cependant, lorsque le thread de production libère la condition sur laquelle le worker attend, un délai de presque une seconde est observé avant que le thread de travail ne prenne le contrôle et s'exécute à nouveau. Je sais cela parce que juste avant que le producteur ne libère la condition sur laquelle le travailleur attend, il fait un mandrin de traitement pour le travailleur s'il est temps de traiter un autre mandrin, puis immédiatement après avoir reçu la condition dans le thread de travail , il fait aussi un mandrin de traitement s'il est temps de traiter un autre mandrin.Comment puis-je améliorer mon comportement en temps réel dans une application multi-thread en utilisant des pthreads et des variables de condition?

Dans ce dernier cas, je constate que je suis en train de traiter plusieurs fois le mandrin. Je voudrais éliminer cette efficacité perdue et faire ce que je peux pour garder les mandrins aussi loin que possible de la fréquence désirée.

Y a-t-il quelque chose que je peux faire pour réduire le délai entre la condition de libération du producteur et la détection que cette condition est libérée de sorte que le travailleur reprenne le traitement? Par exemple, est-ce que cela aiderait le producteur à appeler quelque chose pour se forcer à changer de contexte? La ligne de fond est que le travailleur doit attendre chaque fois qu'il demande au producteur de créer du travail pour lui-même afin que le producteur puisse manipuler les structures de données du travailleur avant de dire au travailleur qu'il est prêt à fonctionner de nouveau en parallèle. Cette période d'accès exclusif par le producteur est censée être courte, mais pendant cette période, je vérifie également le travail en temps réel à effectuer par le producteur au nom du travailleur pendant que le producteur a un accès exclusif. D'une façon ou d'une autre, mon retour à la course en parallèle entraîne de temps en temps des retards importants que je voudrais éviter. Veuillez suggérer comment cela pourrait être mieux accompli.

+0

Je ne comprends pas - et je ne pense pas qu'il y ait suffisamment de détails pour comprendre - pourquoi il y a deux threads. Si c'est une chose entrée-processus-sortie, peut-être que ce peut être un seul thread avec de grands tampons? – wallyk

+0

Il existe deux threads pour une meilleure performance de débit. Un fil produit le travail. Les autres processus de threads qui fonctionnent. Avec un seul fil, la production du travail ne suit pas l'objectif en temps réel de la fréquence de traitement du travail. – WilliamKF

Répondre

2

Je pourrais suggérer le modèle suivant. En général, la même technique pourrait être utilisée, par ex. lors de pré-tamponner des images dans certains moteurs de rendu en temps réel ou quelque chose comme ça. Tout d'abord, il est évident que l'approche que vous décrivez dans votre message ne sera que en vigueur si vos deux threads sont chargés de manière égale (ou presque) tout le temps. Si ce n'est pas le cas, le multi-threading bénéficierait réellement de votre situation. Maintenant, réfléchissons à un motif de fil qui serait optimal pour votre problème. Supposons que nous ayons un yielding et un fil processing. Le premier d'entre eux prépare des morceaux de données à traiter, le second rend le traitement et stocke le résultat du traitement quelque part (pas réellement important).

Le moyen efficace de faire travailler ces fils ensemble est le bon mécanisme de rendement. Votre thread yielding devrait simplement ajouter des données à un tampon partagé et ne devrait pas vraiment se soucier de ce qui se passerait avec ces données. Et, bien, votre tampon pourrait être implémenté comme une simple file d'attente FIFO. Cela signifie que votre fil yielding doit préparer les données à traiter et faire un appel à votre file d'attente PUSH:

X = PREPARE_DATA() 
BUFFER.LOCK() 
BUFFER.PUSH(X) 
BUFFER.UNLOCK() 

Maintenant, le fil processing.Il est le comportement doit être décrit de cette façon (vous devriez probablement ajouter un peu de retard artificiel comme SLEEP(X) entre les appels à EMPTY)

IF !EMPTY(BUFFER) PROCESS(BUFFER.TOP) 

Le moment important est ici ce que si votre thread de traitement des données traitées faire avec. L'approche évidente signifie faire un appel POP après le traitement des données, mais vous voudrez probablement venir avec une meilleure idée. Quoi qu'il en soit, cela dans ma variante ressembler à

// After data is processed 
BUFFER.LOCK() 
BUFFER.POP() 
BUFFER.UNLOCK() 

Notez que les opérations de verrouillage dans yielding et processing threads ne devraient pas avoir un impact réellement votre performance, car ils ne sont appelés une fois par bloc de données.


Maintenant, la partie intéressante. Comme je l'ai écrit au début, cette approche ne serait efficace que si les threads agissent un peu de la même manière en termes d'utilisation CPU/ressources. Il existe un moyen de rendre cette solution de threading efficace même si cette condition n'est pas toujours vraie et qu'elle est importante dans d'autres conditions d'exécution. De cette façon, vous devez créer un autre thread appelé fil controller. Ce thread comparerait simplement le temps que chaque thread utilise pour traiter un morceau de données et équilibrer les priorités de thread en conséquence. En fait, nous ne devons pas « comparer le temps », le fil controller pourrait simplement travailler comme comme:

IF BUFFER.SIZE() > T 
    DECREASE_PRIORITY(YIELDING_THREAD) 
    INCREASE_PRIORITY(PROCESSING_THREAD) 

Bien sûr, vous pourriez mettre en œuvre des meilleures heuristiques ici, mais l'approche avec controller fil devrait être clair.