Comment connecter de manière asynchrone?
Je suis en utilisant Entreprise de la Bibliothèque 4 sur un de mes projets pour l'enregistrement (et à d'autres fins). J'ai remarqué qu'il y a un certain coût pour l'enregistrement que je fais que je puisse atténuer en faisant la journalisation sur un thread séparé.
La façon dont je le fais maintenant, c'est que j'ai créer un LogEntry objet, puis-je appeler BeginInvoke un délégué qui appelle Enregistreur.Écrire.
new Action<LogEntry>(Logger.Write).BeginInvoke(le, null, null);
Ce que j'aimerais vraiment faire est d'ajouter le journal de message à une file d'attente et ensuite avoir un seul thread tirant LogEntry instances de la file d'attente et la réalisation de l'opération de journal. L'avantage de ce serait que la journalisation est de ne pas interférer avec l'exécution de l'opération et non pas chaque opération d'enregistrement des résultats dans un emploi projeté sur le pool de threads.
Comment puis-je créer une file d'attente partagée qui prend en charge de nombreux écrivains et un lecteur dans un thread de façon sécuritaire? Quelques exemples d'une file d'attente de mise en œuvre qui vise à soutenir de nombreux écrivains (sans causer de synchronisation/bloquer) et un seul lecteur serait vraiment apprécié.
Recommandation sur des approches alternatives serait aussi apprécié, je ne suis pas intéressé à changer la journalisation des cadres bien.
- J'ai ajouté une version légèrement améliorée, gardez à l'esprit que vous aurez à tester beaucoup avant de l'utiliser, car je viens juste d'en il.
- Bien que n'étant pas directement applicable à cette question (puisqu'il parle EntLib 4), Bibliothèque d'Entreprise 6 prend désormais en charge Asynchrone de vous déconnecter de la boîte.
Vous devez vous connecter pour publier un commentaire.
J'ai écrit ce code a tout à l'arrière, n'hésitez pas à l'utiliser.
Certains avantages:
Ici est une version légèrement améliorée, gardez à l'esprit, j'ai joué très peu de tests sur elle, mais il répond à quelques questions mineures.
Avantages par rapport à l'original:
waiting.Set();
dansProcessQueue()
? Le thread est bloqué de sorte que vous appelez un "ensemble"?Flush() { bool finished=false; queue.Enqueue(() => finished=true); while (!finished) waiting.waitOne(); }
Oui, vous avez besoin d'un producteur/consommateur file d'attente. J'ai un exemple dans mon threading tutoriel - si vous regardez mon "les blocages /moniteur méthodes" page, vous trouverez le code dans la seconde moitié.
Il y a beaucoup d'autres exemples en ligne, bien sûr - et .NET 4.0 sera livré avec un dans le cadre de trop (un peu plus complet que le mien!). Dans .NET 4.0, vous auriez probablement envelopper un
ConcurrentQueue<T>
dans unBlockingCollection<T>
.La version de la page est non-générique (il a été écrit un long il y a le temps), mais vous voudrez probablement pour le rendre générique, il serait trivial à faire.
Que vous appelez
Produce
de chaque "normal" thread, etConsume
à partir d'un fil, en boucle autour et la journalisation de ce qu'il consomme. Il est probablement plus facile juste pour rendre le consommateur thread un thread d'arrière-plan, de sorte que vous n'avez pas besoin de s'inquiéter à propos de "l'arrêt" de la file d'attente lorsque votre application se ferme. Cela ne signifie en rien une distance possibilité de manquer la dernière entrée du journal des si (si c'est la moitié du chemin, écrit-il lorsque l'application sorties) ou même plus si vous êtes à la production plus rapide qu'il peut consommer/log.Je suggère à commencer par la mesure de la performance réelle de l'impact de l'exploitation forestière sur l'ensemble du système (c'est à dire en exécutant profiler) et éventuellement de commutation à quelque chose de plus comme log4net (j'ai personnellement migré à partir de EntLib de l'enregistrement d'un il ya longtemps).
Si cela ne fonctionne pas, vous pouvez essayer d'utiliser cette méthode simple de .NET Framework:
MSDN Détails
Si cela ne fonctionne pas, soit vous pouvez alors recourir à quelque chose comme John Skeet a offert et en fait le code de la async journalisation vous-même.
Voici ce que j'ai trouvé... aussi voir Sam le Safran de la réponse. Cette réponse est wiki de la communauté dans le cas où il y a des problèmes que les gens voient dans le code et souhaitez mettre à jour.
En réponse à Sam Safrons post, je voulais appeler flush et assurez-vous que tout était vraiment fini d'écrire. Dans mon cas, je suis en train d'écrire à une base de données dans la file d'attente thread et tous mes journaux d'événements ont été l'obtention de la file d'attente, mais parfois, l'application a cessé avant tout était fini d'écrire qui n'est pas acceptable dans ma situation. J'ai changé plusieurs morceaux de votre code, mais la principale chose que je voulais partager a la flush:
J'espère que sauve quelqu'un une certaine frustration. C'est particulièrement évident en parallèle des processus de journalisation des lots de données.
Merci pour le partage de votre solution, il m'ont mis dans une bonne direction!
--Johnny S
Je voulais dire que mon post précédent était un peu inutile. Vous pouvez tout simplement mis AutoFlush de vrai et vous n'aurez pas à faire une boucle par tous les auditeurs. Cependant, j'ai toujours eu un fou problème avec les fils parallèles d'essayer de vider l'enregistreur d'événements. J'ai dû créer un autre booléen qui a été définie à true lors de la copie de la file d'attente et l'exécution de la LogEntry écrit et ensuite dans l'éclat de la routine, j'ai dû vérifier que booléenne assurez-vous que quelque chose n'était pas déjà dans la file d'attente et rien n'a été traitées avant de revenir.
Maintenant plusieurs threads en parallèle peut frapper cette chose et quand j'ai appeler flush je sais que c'est vraiment vidé.
Juste une mise à jour:
L'aide d'enteprise bibliothèque 5.0 avec .NET 4.0, il peut facilement être fait par:
Voir:
http://randypaulo.wordpress.com/2011/07/28/c-enterprise-library-asynchronous-logging/
Task.Run
comme ça. C'est essentiellement en transformant un appel synchrone d'un faux méthode asynchrone. On doit s'appuyer sur une véritable méthode asynchrone si la bibliothèque prend en charge il et de l'appeler à l'aide async/await mots-clés (post C# 5.0). Voir blog.stephencleary.com/2013/11/... et blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx. Juste pour dire que c'est OK, ne c'est du boulot, mais pas idéal.Un niveau supplémentaire d'indirection peut aider à ici.
Votre premier appel de méthode asynchrone peut mettre des messages sur un synchonized File d'attente et de définir un événement -- donc, les écluses sont passe dans le fil de la piscine, pas sur votre threads de travail -- et puis encore un autre thread de retirer les messages de la file d'attente lorsque l'événement est déclenché.
Si vous vous connectez quelque chose sur un thread séparé, le message ne peut pas être écrit si l'application se bloque, ce qui le rend assez inutile.
La raison va pourquoi vous devriez toujours vous rincer après chaque écrite entrée.
Si ce que vous avez à l'esprit est une file d'attente PARTAGÉE, alors je pense que vous allez avoir à synchroniser l'écrit, la pousse et la pop.
Mais, je pense que cela en vaut la peine visant à la file d'attente partagée de la conception. En comparaison de l'e /s de l'exploitation forestière et probablement en comparaison avec les autres travaux de votre application est en train de faire, une brève quantité de blocage pour la pousse et la pop ne sera probablement pas significative.