Je sais "Pourquoi mon cadre est-il comme / pas comme XYZ?" Les questions sont un peu dangereuses mais je veux voir ce qui me manque.
dans Winforms, vous ne pouvez pas mettre à jour l'interface utilisateur d'un autre fil. La plupart des gens utilisent Ce modèle : P>
public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke { if (isi.InvokeRequired) { IAsyncResult result = isi.BeginInvoke(call, new object[] { isi }); object endResult = isi.EndInvoke(result); return (TResult)endResult; } else return call(isi); } public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke { if (isi.InvokeRequired) isi.BeginInvoke(call, new object[] { isi }); else call(isi); }
5 Réponses :
Je pense que le problème est que cette construction aurait probablement besoin d'être intégrée à tous les biens em> de tous les composants de l'interface utilisateur dans le cadre. Cela devra également être fait par des développeurs tiers en faisant de telles composantes. P>
Une autre option peut être que les compilateurs ont ajouté la construction autour d'un accès aux composants de l'interface utilisateur, mais cela ajouterait la complexité aux compilateurs à la place. Si je comprends bien, pour une fonctionnalité de faire place dans le compilateur, il devrait P>
Dans ce cas particulier, le compilateur devrait également avoir un moyen de déterminer si un type dans le code est un type qui nécessite la construction de la synchronisation autour de celle-ci ou non. P>
Bien sûr, toutes ces spéculations sont des spéculations, mais je pouvais imaginer que ce type de raisonnement réside derrière la décision. P>
Très bonne réponse. Un détail: en utilisant les méthodes d'extension (à partir de l'exemple de 2e code), il n'aurait pas besoin d'être intégré à chaque propriété.
It est em> intégré, la classe de l'entreprise d'arrière-plan l'implémente automatiquement. Ses manutentionnaires d'événements fonctionnent sur le fil de l'interface utilisateur, en supposant qu'il soit correctement créé. P>
Prendre une approche plus cynique de ceci: c'est un anti-motif. Au moins dans mon code, il est extrêmement em> rare que la méthode même em> fonctionne à la fois sur le fil de l'interface utilisateur et du fil de travail. Il n'a pas de sens de tester invoquée, je sais que c'est toujours vrai depuis que j'ai écrit le code qui l'appellerait intentionnellement em> comme code qui fonctionne dans un fil séparé. p>
Avec toute la plomberie nécessaire nécessaire pour rendre ce code sans danger et interoper correctement. Utilisation de l'instruction de verrouillage et du manuel / des rétives automatiques pour signaler entre les threads. Et si invoquée serait faux em>, je sais que j'ai un bogue dans ce code. Parce que vous invoquer au fil de l'interface utilisateur lorsque les composants de l'interface utilisateur ne sont pas encore créés ou disposés est très mauvais. Il appartient à un appel debug.Assert () au mieux. P>
Je vois que votre point de vue est-il éventuellement un anti-motif. Un détail sur la classe Travailleur de fond: La mise à jour de l'interface utilisateur croisée n'est pas intégrée à Dowork code>. Pour obtenir leur optimisation, vous devez uniquement mettre à jour l'interface utilisateur de
ProgressChanged code> (ou
terminé code>). Je me rends compte que c'est une préférence personnelle, mais je trouve plus facile d'écrire le code de la chaudron et de ne pas avoir à sauter à travers les cerceaux de mise à jour de l'interface utilisateur via
progressanchanged code>.
Notez que invoquionnaire code> est faux jusqu'à ce qu'un contrôle soit ajouté à un formulaire et i> ce formulaire a été montré. Donc, si vous par exemple. Contrôles de test dans les tests d'unité sans les créer réellement ou si vous créez un formulaire au début et l'afficher plus tard, vous avez besoin exactement de ce modèle.
Un autre point: Si vous vous abonnez à un événement d'un autre objet, il peut être judicieux de tester les invoqués code> dans le gestionnaire d'événements. Prenez
FileSystemWatcher code> par exemple - je ne trouve rien dans la documentation que les garanties que les événements de fil seront soulevées à partir. C'est un détail de mise en œuvre. IMO, vous ne devriez pas compter sur un détail de mise en œuvre d'une autre classe. Vous devez donc utiliser le
invoquité requis code> -Pattern.
@nikie - Ce n'est pas la façon dont ça fonctionne. FSW pour une augmentation des événements sur un thread TP, sauf si vous attribuez la propriété SynchronizingObject. Bien documenté. Il faut être, le filetage n'est jamais un détail qui peut être laissé sans papiers. Si pour une raison quelconque, il est sans papiers, alors oui, par tous les moyens, utilisez invoquée.
Un modèle de nettoyage IN.NET 4 utilise TPL et les continuations. Voir lien a> utiliser p> et maintenant, vous pouvez facilement demander que les continuations fonctionnent sur le thread de l'interface utilisateur. P> P>
PDC monte. Je me demande s'ils annonceront quelque chose de pertinent à cette réponse. J'ai vu quelques personnes spéculatives sur l'Eric Lippert's Blog ( blogs.msdn.com/b/ericlippert/archive/2010/10/21/... ) que c # 5 pourrait exister et avoir des fonctionnalités relatives à cela, mais bien sûr ERIC ne peut "ni confirmer ni nier l'existence d'un produit hypothétique" C # 5 "inutilisé ou de tout fonctionnalité hypothétiquement pourrait ou non implémenter."
Si je comprends votre question correctement, vous voudriez que le cadre (ou le compilateur ou un autre morceau de technologie) inclure Invoke / Begininvoke / Endinvoke autour de tous les membres publics d'un objet UI, afin de le faire filer en toute sécurité. Le problème est que seul ne ferait que votre fil de code sûr. Vous devez toujours utiliser Très-unvoke et d'autres mécanismes de syncronisation très souvent. (Voir ceci Grand article sur la sécurité du fil sur Eric Lippert ''s Blog )
Imaginez que vous écrivez le code comme p> si le cadre ou le compilateur Enveloppé tous les accès à Maintenant, vous pourriez dire "mais c'est vrai pour tous les objets mutables partagés, je ' ll juste ajouter des serrures ". Mais c'est assez dangereux. Imaginez que vous ayez fait quelque chose comme: p> Ceci va une impasse: la méthode A est appelée sur un fil d'arrière-plan. Il acquiert la serrure, les appels invoquent. Invoke attend une réponse de la file d'attente du thread de l'UI. Dans le même temps, la méthode B est exécutée dans le thread principal (par exemple dans une touche.Cliquez-le-gestionnaire). L'autre thread contient Recherche et évitant les bugs de filetage comme Celles-ci sont assez difficiles que cela, mais au moins maintenant, vous pouvez voir em> que vous appelez appelez et que c'est un appel de blocage. Si des biens pouvaient bloquer silencieusement, les insectes comme ceux-ci seraient extrémités em> plus difficiles à trouver. P> moral: le filetage est difficile. Le fil-ui-interaction est encore plus difficile, car il n'y a qu'une seule file d'attente partagée unique. Et malheureusement, ni nos compilateurs ni nos cadres sont suffisamment intelligents pour "faire fonctionner juste le bien". P> p> SELECTELITEM code> et l'appel sur
Supprimer code> dans un appel de beginInvoke / Invoquez, ce ne serait pas em> soyez en sécurité. Il y a une condition de course potentielle si
SELECTELITEMEM code> est non nulle lorsque la clause IF est évaluée, mais un autre thread la définit sur NULL avant la fin du blocage de l'époque. Probablement la totalité de la clause if-then-ele-else devrait être enveloppée dans un appel de BeginInvoke, mais comment le compilateur devrait-il le savoir? P>
myListboxLock code>, il ne peut donc pas entrer dans la serrure - maintenant les deux threads attendent les uns sur les autres, ni ne peuvent faire de progrès. P>
Je pensais qu'il serait peut-être intéressant de mentionner pourquoi il s'agit d'un fil d'interface utilisateur en premier lieu. Il s'agit de réduire le coût de la production de composants d'interface utilisateur tout en augmentant leur exactitude et leur robustesse. P>
Le problème de base de la sécurité du thread est qu'une mise à jour non atomique de l'état privé peut être observée à mi-fini sur un thread de lecture si le fil d'écriture est à mi-chemin lorsque la lecture se produit. p>
Pour atteindre la sécurité du fil, vous pouvez faire un certain nombre de choses que vous pouvez faire. p>
1) verrouille explicitement toutes les lectures et écrit. Avantages: maximalement flexible; Tout fonctionne sur n'importe quel fil. Inconvénients: maximalement douloureux; Tout doit être verrouillé tout le temps. Les serrures peuvent être soumis, ce qui les rend lents. Il est très facile d'écrire des blocages. Il est très facile d'écrire du code qui gère la rentrinie malade. Et ainsi de suite. P>
2) Autoriser les lectures et écrit uniquement sur le thread qui a créé l'objet. Vous pouvez avoir plusieurs objets sur plusieurs threads, mais une fois qu'un objet est utilisé sur un thread, c'est le seul fil qui peut l'utiliser. Par conséquent, il n'y aura pas de lecture et écrit simultanément sur différents threads, de sorte que vous n'avez pas besoin de verrouiller quoi que ce soit. Il s'agit du modèle "appartement", et c'est le modèle que la grande majorité des composants de l'interface utilisateur s'attend à attendre. Le seul état qui doit être verrouillé est l'état partagé par plusieurs instances sur différents threads, et c'est assez facile à faire. p>
3) Autorise des lectures et écrit uniquement sur le fil de possession, mais permettez à un fil de donner explicitement la propriété à un autre lorsqu'il n'y a pas de lecture et écrit en cours. Il s'agit du modèle "location" et c'est le modèle utilisé par les pages de serveurs actifs pour recycler les moteurs de script. p>
Étant donné que la grande majorité des composants de l'interface utilisateur sont écrits pour travailler dans le modèle d'appartement, et il est douloureux et difficile de faire tous ces composants à filetage libre, vous êtes coincé avec qui devoir faire tout le travail de l'interface utilisateur sur le fil de l'interface utilisateur. p>
Quelque chose d'un peu lié à votre question est ici, pas comme une réponse, mais juste pour vous de lire: Stackoverflow.com/questions/3972727/...
Voir aussi Stackoverflow.com/questions/3768954/...