Récemment, j'ai essayé d'accéder à une zone de texte à partir d'un fil (autre que le fil de l'interface utilisateur) et une exception a été levée. Il disait quelque chose sur le fait que "le code n'était pas thread-safe" et j'ai donc fini par écrire un délégué (exemple fourni par MSDN) et à l'appel à la place.
Mais malgré cela, je ne comprenais pas vraiment pourquoi tout ce code supplémentaire était nécessaire.
Mise à jour: vais-je rencontrer des problèmes graves si je vérifie
Controls.CheckForIllegalCrossThread..blah =true
Eric Lippert a publié un article dans le blog Nice intitulé Quelle est cette chose que vous appelez "thread safe"? à propos de la définition de la sécurité des threads dans Wikipedia.
3 choses importantes extraites des liens:
"Un morceau de code est thread-safe s'il fonctionne correctement lors de l'exécution simultanée par plusieurs threads."
"En particulier, il doit satisfaire le besoin de plusieurs threads d'accéder aux mêmes données partagées,…"
“… Et la nécessité d'accéder à un élément de données partagé par un seul thread à la fois.”
Vaut vraiment la peine d'être lu!
Dans le plus simple des termes threadsafe signifie qu'il est sûr d'être accessible à partir de plusieurs threads. Lorsque vous utilisez plusieurs threads dans un programme et que chacun tente d'accéder à une structure de données commune ou à un emplacement en mémoire, plusieurs problèmes peuvent se produire. Donc, vous ajoutez du code supplémentaire pour éviter ces mauvaises choses. Par exemple, si deux personnes écrivaient le même document en même temps, la deuxième personne à sauvegarder remplacera le travail de la première personne. Pour que le fil soit sécurisé, vous devez forcer la personne 2 à attendre que la personne 1 termine sa tâche avant de permettre à la personne 2 de modifier le document.
Wikipedia a un article sur la sécurité des threads.
Ceci page de définitions (vous devez ignorer une annonce - désolé) la définit ainsi:
En programmation informatique, thread-safe décrit une partie de programme ou une routine pouvant être appelée à partir de plusieurs threads de programmation sans interaction indésirable entre les threads.
Un thread est un chemin d'exécution d'un programme. Un programme à un seul thread ne comportera qu'un seul thread et le problème ne se pose donc pas. Pratiquement tous les programmes de l’interface graphique ont plusieurs chemins d’exécution et donc des unités d’exécution - il y en a au moins deux, un pour le traitement de l’affichage de l’interface graphique et le traitement des entrées utilisateur, et au moins un autre pour effectuer les opérations du programme.
Ceci est fait pour que l'interface utilisateur soit toujours réactive pendant que le programme fonctionne en déchargeant tout processus de longue durée sur des threads non-d'interface utilisateur. Ces threads peuvent être créés une fois et exister pendant toute la durée du programme, ou simplement être créés à la demande et détruits à la fin.
Etant donné que ces threads devront souvent effectuer des actions communes - entrée/sortie disque, affichage des résultats à l'écran, etc. - ces parties du code devront être écrites de manière à pouvoir être appelées à partir de plusieurs threads, souvent à le même temps. Cela impliquera des choses comme:
Tout simplement thread-safe signifie qu'une instance de méthode ou de classe peut être utilisée par plusieurs threads en même temps sans aucun problème.
Considérez la méthode suivante:
private int myInt = 0;
public int AddOne()
{
int tmp = myInt;
tmp = tmp + 1;
myInt = tmp;
return tmp;
}
Maintenant, les threads A et B voudraient tous deux exécuter AddOne (). mais A commence en premier et lit la valeur de myInt (0) dans tmp. Maintenant, pour une raison quelconque, le planificateur décide d’arrêter le thread A et de différer l’exécution du thread B. Le thread B lit également la valeur de myInt (toujours 0) dans sa propre variable tmp. Le thread B termine la méthode entière, donc myInt = 1 à la fin. 1 est renvoyé. Maintenant, c'est à nouveau le fil A. Le fil A continue. Et ajoute 1 à tmp (tmp était 0 pour le fil A). Et enregistre ensuite cette valeur dans myInt. myInt est encore 1.
Donc, dans ce cas, la méthode AddOne a été appelée deux fois, mais comme la méthode n'a pas été implémentée de manière sécurisée pour les threads, la valeur de myInt n'est pas 2, comme prévu, mais 1 car le second thread a lu la variable myInt avant la fin du premier thread. mise à jour.
La création de méthodes thread-safe est très difficile dans des cas non triviaux. Et il y a pas mal de techniques. Dans Java vous pouvez marquer une méthode comme synchronisée, cela signifie qu'un seul thread peut exécuter cette méthode à un moment donné. Les autres threads attendent en ligne. Cela sécurise un thread de méthode, mais si il y a beaucoup de travail à faire dans une méthode, cela gaspille beaucoup d’espace. Une autre technique consiste à 'marque seulement une petite partie d’une méthode comme synchronisée' en créant un verrou ou un sémaphore , et verrouille cette petite partie (généralement appelée la section critique). Il existe même certaines méthodes implémentées avec le thread-safe sans verrou, ce qui signifie qu'elles sont construites de manière à ce que plusieurs threads puissent les parcourir en même temps. sans jamais causer de problèmes, cela peut être le cas lorsqu'une méthode n'exécute qu'un seul appel atomique, appels qui ne peuvent pas être interrompus et ne peuvent être effectués que par un seul thread à la fois.
Un module est thread-safe s'il garantit qu'il peut conserver ses invariants face aux utilisations multithreads et concurrentes.
Ici, un module peut être une structure de données, une classe, un objet, une méthode/procédure ou une fonction. Morceau de code et données associées à la base.
La garantie peut éventuellement être limitée à certains environnements, telle qu'une architecture de processeur spécifique, mais doit être valable pour ces environnements. S'il n'y a pas de délimitation explicite des environnements, cela signifie généralement que cela vaut pour tous les environnements dans lesquels le code peut être compilé et exécuté.
Les modules Thread-unsafe peuvent fonctionnent correctement sous une utilisation simultanée et multithread, mais ceci est souvent plus dû à la chance et à la coïncidence qu'une conception minutieuse. Même si certains modules ne se cassent pas pour vous, ils peuvent casser lorsqu'ils sont déplacés vers d'autres environnements.
Les bogues multi-threading sont souvent difficiles à déboguer. Certaines ne se produisent qu'occasionnellement, alors que d'autres se manifestent de manière agressive - cela aussi peut être spécifique à l'environnement. Ils peuvent se manifester par des résultats subtilement erronés ou des blocages. Ils peuvent endommager les structures de données de manière imprévisible et faire apparaître d'autres bogues apparemment impossibles dans d'autres parties distantes du code. Il peut être très spécifique à une application, il est donc difficile de donner une description générale.
Dans le monde réel, l'exemple pour le profane est
Supposons que vous ayez un compte bancaire sur Internet et les services bancaires mobiles et que votre compte ne compte que 10 $. Vous avez transféré le solde sur un autre compte en utilisant les services bancaires mobiles et, entre-temps, vous avez effectué des achats en ligne en utilisant le même compte bancaire. Si ce compte bancaire n'est pas threadsafe, la banque vous autorise à effectuer deux transactions en même temps et la banque sera alors en faillite.
Threadsafe signifie que l'état d'un objet ne change pas si plusieurs threads tentent simultanément d'accéder à l'objet.
Vous pouvez obtenir plus d'explications dans le livre "Java Concurrency in Practice":
Une classe est thread-safe si elle se comporte correctement lorsqu'elle est accédée à partir de plusieurs threads, indépendamment de la planification ou de l'entrelacement de l'exécution de ces threads par l'environnement d'exécution et sans synchronisation ou autre coordination de la part du code appelant.
Sécurité des threads: Un programme thread-safe protège ses données contre les erreurs de cohérence de la mémoire. Dans un programme hautement multithread, un programme thread-safe ne provoque aucun effet secondaire avec plusieurs opérations de lecture/écriture à partir de plusieurs threads sur les mêmes objets. Différents threads peuvent partager et modifier les données d'objet sans erreurs de cohérence.
Vous pouvez assurer la sécurité des threads en utilisant une API de simultanéité avancée. Cette documentation page fournit de bonnes constructions de programmation pour assurer la sécurité des threads.
( Verrouiller les objets prend en charge les idiomes de verrouillage qui simplifient de nombreuses applications simultanées.
Les exécuteurs définissent une API de haut niveau pour le lancement et la gestion des threads. Les implémentations d'exécuteur fournies par Java.util.concurrent fournissent une gestion de pool de threads adaptée aux applications à grande échelle.
Les collections simultanées facilitent la gestion de grandes collections de données et permettent de réduire considérablement le besoin de synchronisation.
Variables atomiques ont des fonctionnalités qui minimisent la synchronisation et permettent d'éviter les erreurs de cohérence de la mémoire.
ThreadLocalRandom (dans JDK 7) permet de générer efficacement des nombres pseudo-aléatoires à partir de plusieurs threads.
Reportez-vous aux packages Java.util.concurrent et Java.util.concurrent.atomic pour les autres constructions de programmation.
Je trouve que le concept de http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 est ce que je considère habituellement comme un threading non sécurisé, c'est-à-dire lorsqu'une méthode a et repose sur un côté effet tel qu'une variable globale.
Par exemple, j'ai vu du code formatant des nombres à virgule flottante en chaîne. Si deux d'entre eux sont exécutés dans des threads différents, la valeur globale de decimalSeparator peut être définitivement modifiée en '.'
//built in global set to locale specific value (here a comma)
decimalSeparator = ','
function FormatDot(value : real):
//save the current decimal character
temp = decimalSeparator
//set the global value to be
decimalSeparator = '.'
//format() uses decimalSeparator behind the scenes
result = format(value)
//Put the original value back
decimalSeparator = temp
Vous travaillez clairement dans un environnement WinForms. Les contrôles WinForms présentent une affinité de thread, ce qui signifie que le thread dans lequel ils sont créés est le seul thread pouvant être utilisé pour y accéder et les mettre à jour. C'est pourquoi vous trouverez des exemples sur MSDN et ailleurs démontrant comment réorganiser l'appel sur le thread principal.
L’usage normal de WinForms est d’avoir un seul thread dédié à l’ensemble de votre travail d’interface utilisateur.