C# Multithreading — Invoquer sans Contrôle
Je suis seulement un peu familier avec le multi-threading et que j'ai lu à ce sujet, mais ne l'ai jamais utilisé en pratique.
J'ai un projet qui utilise un tiers de la bibliothèque que les actions de l'état d'un périphérique d'entrée par le déclenchement des événements. Le problème, c'est la façon dont la bibliothèque est écrit ces événements sont déclenchés par un thread différent.
Ma demande n'a pas besoin d'être multi-thread et j'ai couru dans beaucoup de classiques problèmes de threading (contrôles d'INTERFACE utilisateur se plaindre d'être en interaction avec d'un thread différent, les collections qui sont modifiés comme un morceau de code est une itération sur elle, etc.).
Je veux juste la 3ème partie de la bibliothèque de l'événement pour en revenir à mon thread de l'INTERFACE utilisateur. Plus précisément, je pense que ce qui doit arriver est:
Ma classe reçoit l'événement et le gestionnaire est en cours d'exécution sur un autre thread de l'INTERFACE utilisateur. Je veux détecter cette condition (comme avec InvokeRequired), puis effectuer l'équivalent de BeginInvoke à donner le contrôle de retour pour le thread d'INTERFACE utilisateur. Puis à la bonne notifications peuvent être envoyées sur la hiérarchie de classe et l'ensemble de mes données n'est touché par l'un thread.
Le problème, c'est la classe qui est la réception de ces événements d'entrée n'est pas dérivé de Contrôle et, par conséquent, n'ont pas InvokeRequired ou BeginInvoke. La raison pour cela est que j'ai essayé de séparer proprement de l'INTERFACE utilisateur et la logique sous-jacente. La classe est toujours en cours d'exécution sur le thread de l'INTERFACE utilisateur, il n'a tout simplement pas toute l'INTERFACE utilisateur à l'intérieur de la classe elle-même.
Maintenant que j'ai résolu le problème en les ruinant, que la séparation. Je passe une référence pour le contrôle qui sera d'afficher les données de ma classe et à l'aide de son Appeler des méthodes. Qui semble comme il défait le but entier de se séparer, parce que maintenant la classe sous-jacente a une dépendance directe sur mon INTERFACE spécifique à chaque classe.
Peut-être il y a un moyen pour enregistrer une référence pour le thread qui a couru le constructeur et puis, il y a quelque chose dans le thread de l'espace de noms qui effectue l'Invoquer des commandes?
Est-il un moyen de contourner cela? Mon approche complètement tort?
Vous devez vous connecter pour publier un commentaire.
Regarder dans le
AsyncOperation
classe. Vous créez une instance deAsyncOperation
sur le fil que vous voulez l'appeler le gestionnaire sur l'utilisation de laAsyncOperationManager.CreateOperation
méthode. L'argument que j'utilise pourCreate
est généralement nulle, mais vous pouvez la définir à quoi que ce soit. Pour appeler une méthode sur ce thread, utilisez leAsyncOperation.Post
méthode.Utilisation SynchronizationContext.Actuel, qui pointe à quelque chose que vous pouvez synchroniser avec.
Ce sera faire la bonne chose™ selon le type d'application. Pour une application WinForms, il va l'exécuter sur le main thread d'INTERFACE utilisateur.
Plus précisément, la SynchronizationContext.Envoyer méthode, comme ceci:
SynchronizationContext.Current
est null si le programme est une application de console. Un bon modèle est à l'aide deAsyncOperation
qui capture laSynchronizationContext
sur la création et *fait la bonne chose++*™. msdn.microsoft.com/en-us/library/...La méthode de traitement pourrait simplement stocker les données dans une variable membre de la classe. Le seul problème avec la croix-threading se produit lorsque vous souhaitez mettre à jour des threads pour les contrôles qui ne sont pas créés dans le contexte de thread. Donc, votre classe générique pourrait écouter l'événement, puis d'appeler le contrôle que vous souhaitez mettre à jour avec une fonction de délégué.
Encore, seuls les contrôles de l'INTERFACE utilisateur que vous souhaitez mettre à jour doivent être invoquées pour rendre thread-safe. Il y a un moment, j'ai écrit un billet de blog sur un "La Solution la plus Simple pour Illégaux Appels inter-threads en C#"
La poste va plus dans le détail, mais le coeur d'un très simple (mais limité) approche à l'aide d'un anonyme en fonction de délégué sur le contrôle d'INTERFACE utilisateur que vous souhaitez mettre à jour:
J'espère que cette aide. Le InvokeRequired est techniquement facultatif, mais en Invoquant des contrôles est assez coûteux, de sorte que l'enregistrement s'assurer qu'il ne met pas à jour label1.Texte par l'invoquer si ce n'est pas nécessaire.
Vous n'avez pas besoin de contrôle spécifiques, tout contrôle (y compris la Forme) de le faire. Donc, vous pourriez abstrait, loin de l'INTERFACE utilisateur un peu.
Si vous êtes en utilisant WPF:
Vous avez besoin d'une référence à l'objet Répartiteur qui gère le thread d'INTERFACE utilisateur. Ensuite, vous pouvez utiliser l'Invoquer ou de la méthode BeginInvoke sur le répartiteur objet pour planifier une opération qui prend place dans le thread de l'INTERFACE utilisateur.
La façon la plus simple pour obtenir le répartiteur est l'aide de l'Application.Actuel.Répartiteur. C'est le régulateur responsable de la principale (et probablement la seule) thread d'INTERFACE utilisateur.
Mettant tous ensemble:
Oui, le travail serait pour vous de créer un thread-safe file d'attente.
Dans les deux cas, parce que la file d'attente est écrit par deux threads différents (la 3e partie de thread est enqueueing, et votre fil est dequeueing), il doit être thread-safe, protégé de la file d'attente.
J'ai juste couru dans la même situation. Cependant, dans mon cas, je ne pouvais pas utiliser SynchronizationContext.Courant, parce que je n'ai pas accès aux composants de l'INTERFACE utilisateur et pas de rappel pour capturer l'actuel contexte de synchronisation. Il s'avère que si le code n'est pas en cours d'exécution dans un Windows Forms messge de la pompe, SynchronizationContext.Actuelle est fixée à une norme SynchronizationContext, qui s'exécutera seulement Envoyer des appels sur le thread en cours et après les appels sur le pool de threads.
J'ai trouvé cette réponse expliquant les différents types de synchronisation contextes. Dans mon cas, la solution était de créer une nouvelle WindowsFormsSynchronizationContext objet sur le thread qui seraient par la suite démarrer la pompe de messages à l'aide de l'Application.Run(). Ce contexte de synchronisation peut être utilisée par d'autres threads pour exécuter du code sur le thread de l'INTERFACE utilisateur, sans jamais toucher tous les composants de l'INTERFACE utilisateur.