J'essaie de faire une situation de producteur / de culture de consommation plus efficace en sautant des opérations d'événement coûteuses si nécessaire avec quelque chose comme:
//cas(variable, compare, set) is atomic compare and swap //queue is already lock free running = false // dd item to queue â producer thread(s) if(cas(running, false, true)) { // We effectively obtained a lock on signalling the event add_to_queue() signal_event() } else { // Most of the time if things are busy we should not be signalling the event add_to_queue() if(cas(running, false, true)) signal_event() } ... // Process queue, single consumer thread reset_event() while(1) { wait_for_auto_reset_event() // Preferably IOCP for(int i = 0; i < SpinCount; ++i) process_queue() cas(running, true, false) if(queue_not_empty()) if(cas(running, false, true)) signal_event() }
4 Réponses :
est allé à travers un tas de cas, je ne peux pas voir un problème. Mais c'est un peu compliqué. Je pensais que vous auriez peut-être un problème avec Queue_Not_empty / add_to_queue Racing. Mais on dirait que le CAS post-dominant dans les deux chemins couvre ce cas.
CAS est coûteux (pas aussi cher que le signal). Si vous vous attendez à sauter le signal d'être courant, je coderais le CAS comme suit: P>
running = false // add item to queue â producer thread(s) add_to_queue() if (cas(running, false, true)) { signal_event() } // Process queue, single consumer thread reset_event() while(1) { wait_for_auto_reset_event() // Preferably IOCP for(int i = 0; i < SpinCount; ++i) process_queue() cas(running, true, false) // this could just be a memory barriered store of false if(queue_not_empty()) if(cas(running, false, true)) signal_event() }
Désolé j'ai manqué tous ces commentaires jusqu'à présent - merci pour le vôtre! - Et je prends juste le temps de digérer ce que vous avez dit maintenant ...
Sur la première partie de votre commentaire (comme je commence à me souvenir de mon train de réflexion d'avant!) - Oui, je l'ai structuré autour de cette façon (je pense!) Pour faire face à une condition de course que je me souviens d'être causée dans un exemple comme La réponse d'Anthony. Je pense que j'allais pour le moindre des maux - en demandant au travailleur dans certains cas, boucle peut-être plus de fois que nécessaire au pire des cas.Je digestion de votre suggestion plus maintenant et votre commentaire sur le magasin de mémoire de mémoire que je me demandais auparavant Quelque chose comme ça, mais décider d'essayer de le faire travailler avec des choses verrouillées d'abord :)
Pourquoi ne pas simplement associer un de cette façon, vous n'attendez que s'il n'y a pas d'éléments sur la file d'attente et que vous signalez uniquement l'événement une fois pour chaque lot d'éléments. p> p> bool code> avec l'événement? Utilisez
CAS code> pour le définir sur true, et si le
CAS code> réussit alors signalez l'événement car l'événement doit avoir été clair. Le serveur peut alors simplement effacer le drapeau avant d'éviter
Désolé ignore mon dernier commentaire que j'ai frappé le retour trop vite! - Je pense que j'ai essayé quelque chose comme ça, mais j'ai travaillé là-bas, il y avait une condition de course avec elle entre le bit d'éclair et la file d'attente étant vide.
Je crois, vous voulez réaliser quelque chose comme dans cette question:
de
WinForms multithreading: exécuter une interface graphique Mise à jour uniquement si le précédent est terminé. Il est spécifique sur C # et Winforms, mais la structure peut bien s'appliquer à vous. P>
Cela tombe dans la sous-catégorie de "Arrêtez de jouer et retourner au travail" appelé "Optimisation prématurée". : -)
Si les opérations d'événement "coûteuses" occupent une partie de temps importante, votre conception est fausse et plutôt que d'utiliser un producteur / consommateur, vous devez utiliser une section critique / mutex et faire le travail de la Appelant le fil. P>
Je vous suggère de profil de votre application si vous êtes vraiment concerné. P>
: strong> p> producteur p> consommateur p> notes: p> hypothèses: p>
Je suis désolé mais c'est une réponse assez mauvaise, même si j'apprécie que vous essayez d'aider. Les sections critiques et les mutiles sont trop lentes pour le type de débit que je travaille. Beaucoup de celles-ci provient des commutateurs contextuels du système d'exploitation pouvant arriver.
@WB, vous réinventez simplement la section critique. C'est précisément que les sections critiques de problèmes sont conçues pour résoudre, alors utilisez-les simplement. Ils ne bloquent pas à moins d'avoir une affirmation b>
Pour être clair, votre solution proposée consiste à utiliser une barrière de mémoire / comparer et échanger et échanger des opérations pour éviter de signaler ou d'attendre un objet / un sémaphore d'événement. C'est exactement ce que font les sections critiques et votre exemple est exactement la raison pour laquelle ils le font. C'est pourquoi la réponse à votre question est "Utiliser une section critique" - pas parce que quelque chose ne va pas avec votre idée, mais parce que cela a déjà été inventé.
Dans quelle langue votre code est-il votre code?
Les opérations d'événements sont-elles coûteuses ou le traitement du fil de consommation coûtent-ils également cher? (Et vous voulez éviter cela aussi?)
Désolé, j'ai juste remarqué que tous ces commentaires maintenant! C'est en C ++, bien que je suis intéressé à répliqué similaire à Csharp. Une opération de synchronisation des événements est la chose la plus chère si elle est prise pour chaque élément de la file d'attente.