Que signifie threadsafe?

récemment j'ai essayé d'accéder à une boîte de texte à partir d'un thread (autre que le thread UI) et une exception a été lancée. Il a dit quelque chose à propos du "code n'étant pas sûr thread" et donc j'ai fini par écrire un délégué (échantillon de MSDN aidé) et l'appeler à la place.

mais je n'ai pas tout à fait compris pourquoi tout le code supplémentaire était nécessaire.

mise à jour: Vais - je rencontrer des problèmes graves si je vérifie

Controls.CheckForIllegalCrossThread..blah =true
92
demandé sur Vivek Bernard 2010-01-09 18:43:35

11 réponses

Eric Lippert a un bel article de blog intitulé Qu'est-ce que cette chose que vous appelez "thread safe"? à propos de la définition de la sécurité des fils trouvée dans Wikipedia.

3 choses importantes extraites des liens:

"un morceau de code est sans fil s'il fonctionne correctement pendant exécution simultanée par plusieurs threads."

, "En particulier, il doit satisfaire la nécessité de fils multiples pour accéder aux mêmes données partagées, ..."

"...et la nécessité du partage des morceau de données accessibles par un seul le fil à un moment donné."

mérite certainement une lecture!

93
répondu Gregory Pakosz 2017-05-23 12:10:27

dans le plus simple des termes threadsafe signifie qu'il est sûr d'être accédé à partir de plusieurs threads. Lorsque vous utilisez plusieurs threads dans un programme et qu'ils tentent chacun d'accéder à une structure de données commune ou à un emplacement dans la mémoire, plusieurs mauvaises choses peuvent se produire. Donc, vous ajoutez du code supplémentaire pour empêcher ces mauvaises choses. Par exemple, si deux personnes écrivaient le même document en même temps, la seconde personne à sauver écraserait le travail de la première personne. Pour faire de ce thread donc, 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.

79
répondu Vincent Ramdhanie 2018-06-24 15:31:07

Wikipedia a un article sur la sécurité des fils.

Ce définitions page (vous devez passer une annonce - désolé) le définit ainsi:

dans la programmation informatique, thread-safe décrit une portion de programme ou de routine qui peut être appelé à partir de multiples threads de programmation sans interaction indésirable entre les threads.

un fil est une exécution chemin d'accès d'un programme. Un seul programme fileté n'aura qu'un seul thread et donc ce problème ne se pose pas. Pratiquement tous les programmes GUI ont des chemins d'exécution multiples et donc des threads - un pour le traitement de l'affichage de l'interface graphique et la gestion des entrées de l'utilisateur, d'autres pour effectuer effectivement les opérations du programme. C'est ainsi que l'assurance-chômage demeure réceptive pendant que le programme fonctionne.

13
répondu ChrisF 2010-01-09 15:46:32

un module est thread-safe s'il garantit qu'il peut maintenir ses invariants face à l'utilisation de multi-threads et de concurrence.

ici, un module peut être une structure de données, une classe, un objet, une méthode/procédure ou une fonction. Essentiellement scoped morceau de code et les données connexes.

la garantie peut potentiellement être limitée à certains environnements tels qu'une architecture CPU spécifique, mais doit être valable pour ces environnements. Si il n'est pas explicite la délimitation des environnements, puis il est généralement pris pour impliquer qu'il tient pour tous les environnements que le code peut être compilé et exécuté.

Thread-unsafe modules may fonction correctement sous multi-threaded et l'utilisation simultanée, mais ce est souvent plus de chance et de coïncidence, que la conception soigneuse. Même si un module ne se casse pas pour vous en dessous, il peut se casser lorsqu'il est déplacé vers d'autres environnements.

multi-threading les bogues sont souvent difficiles à corriger. Certains d'entre eux ne se produisent qu'occasionnellement, tandis que d'autres se manifestent de façon agressive - cela aussi, peut être spécifique à l'environnement. Ils peuvent se manifester comme des résultats subtilement erronés, ou des blocages. Ils peuvent gâcher 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 à l'application, il est donc difficile de donner une description générale.

4
répondu Chris Vest 2010-01-09 16:15:03

tout Simplement , thread-safe signifie qu'une méthode ou une instance de classe peut être utilisée par plusieurs threads en même temps sans problèmes.

envisager la méthode suivante:

private int myInt = 0;
public int AddOne()
{
    int tmp = myInt;
    tmp = tmp + 1;
    myInt = tmp;
    return tmp;
}

maintenant thread A et thread B veulent 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 reporter l'exécution au thread B. Le Thread B Lit maintenant aussi la valeur de myInt (toujours 0) dans sa propre variable tmp. Le fil B termine toute la méthode, donc à la fin myInt = 1. Et 1 est retourné. Maintenant, C'est à nouveau le tour de Thread A. Enfiler Une continue. Et ajoute 1 à tmp (tmp était 0 pour le thread A). Et garde cette valeur dans myInt. myInt est encore 1.

ainsi dans ce cas la méthode AddOne a été appelée deux fois, mais parce que la méthode n'a pas été mise en œuvre d'une manière sûre de filetage la valeur de myInt n'est pas 2, comme prévu, mais 1 parce que le deuxième thread lisez la variable myInt avant que le premier thread ne finisse sa mise à jour.

créer des méthodes sans fil est très difficile dans les cas non triviaux. Et il y a assez peu de techniques. En Java, vous pouvez marquer une méthode synchronisée, ce qui signifie qu'un seul thread peut exécuter cette méthode à un moment donné. Les autres threads attendent en ligne. Cela rend un filetage de méthode sûr, mais s'il ya beaucoup de travail à faire dans une méthode, alors cela gaspille beaucoup d'espace. Un autre la technique consiste à " marquer seulement une petite partie d'une méthode comme synchronisé " en créant une serrure ou un sémaphore, et le verrouillage de cette petite partie (généralement appelé la section critique). Il y a même des méthodes qui sont implémentées comme des threads sans verrouillage, ce qui signifie qu'elles sont construites de telle sorte que plusieurs threads puissent courir à travers elles en même temps sans jamais causer de problèmes, ce qui peut être le cas lorsqu'une méthode n'exécute qu'un seul appel atomique. Atomique appels sont des appels cela ne peut être interrompu et ne peut être fait que par un fil à la fois.

4
répondu Sujith PS 2013-10-22 05:46:20

Vous pouvez obtenir plus d'explications dans le livre "Java Simultanéité dans la Pratique":

une classe est sans thread si elle se comporte correctement lorsqu'elle est accédée à partir de plusieurs threads, indépendamment de la programmation ou de l'interfonctionnement de l'exécution de ces threads par l'environnement runtime, et sans synchronisation supplémentaire ou autre coordination de la part du code appelant.

4
répondu Jacky 2014-07-15 03:04:10

Thread safety : un programme thread safe protège ses données des erreurs de cohérence de la mémoire. Dans un programme hautement multi-threadé, un programme thread safe ne cause 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 des données d'objet sans erreurs de cohérence.

vous pouvez obtenir la sécurité du thread en utilisant l'API de concurrence avancée. Ce documentation page fournit de bonnes constructions de programmation pour atteindre la sécurité de filetage.

les objets de verrouillage supportent des idiomes de verrouillage qui simplifient de nombreuses applications concurrentes.

Exécuteurs définir une API de haut niveau pour le lancement et la gestion des threads. Exécuteur implémentations fournies par Java.util.gestion simultanée du pool de threads approprié pour les applications à grande échelle.

Concurrent Collections rendre plus facile à gérer de grands ensembles de données, et peut réduire considérablement la nécessité pour la synchronisation.

"Variables atomiques ont des caractéristiques qui minimisent la synchronisation et aident à éviter les erreurs de cohérence de la mémoire.

au Royaume-Uni (dans JDK 7) fournit la génération efficace de nombres de pseudorandom à partir de fils multiples.

Consultez de java.util.concurrent et java.util.simultané.les paquets atomiques aussi pour d'autres constructions de programmation.

3
répondu Ravindra babu 2016-05-20 17:26:09

dans le monde réel exemple pour le profane est

supposons que vous avez un compte bancaire avec l'internet et les services bancaires mobiles et votre compte ont seulement 10$. Vous avez effectué le solde de transfert à un autre compte en utilisant la banque mobile et entre-temps vous avez fait des achats en ligne en utilisant le même compte bancaire. Si ce compte bancaire N'est pas" sécurisé", alors la banque vous permet d'effectuer deux transactions identiques et alors la banque fera faillite.

thread-safe dire que l'état de l'Objet ne change pas si simultanément plusieurs threads tentent d'accéder à l'Objet.

3
répondu Yasir Shabbir Choudhary 2017-07-07 07:40:07

vous travaillez clairement dans un environnement WinForms. Les commandes WinForms présentent une affinité de thread, ce qui signifie que le thread dans lequel elles sont créées est le seul thread qui peut être utilisé pour les accéder et les mettre à jour. C'est la raison pour laquelle vous trouverez des exemples sur MSDN et ailleurs montrant comment ramener le rappel sur le fil principal.

normal WinForms pratique est d'avoir un fil simple qui est dédié à tout votre travail UI.

1
répondu David M 2010-01-09 15:48:42

je trouve le concept de http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 pour être ce que je pense habituellement comme filetage dangereux qui est quand une méthode A et repose sur un effet secondaire tel qu'une variable globale.

par exemple, j'ai vu du code qui formatait 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 changée en permanence 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
1
répondu Aaron Robson 2011-11-01 19:07:41

pour comprendre la sécurité des fils, lire sections :

4.3.1. Exemple: Traceur De Véhicule Utilisant La Délégation

comme un exemple plus substantiel de délégation, construisons une version du tracker de véhicule qui délègue à une classe de thread-safe. Nous stockons les emplacements dans une carte, donc nous commençons avec une implémentation de carte sans fil, ConcurrentHashMap . Nous stockons également emplacement utilisant une classe de points immuable au lieu de MutablePoint , indiqué dans la liste 4.6.

liste 4.6. Classe de Point immuable utilisée par DelegatingVehicleTracker.

 class Point{
  public final int x, y;

  public Point() {
        this.x=0; this.y=0;
    }

  public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

}

Point est sans fil parce qu'il est immuable. Les valeurs immuables peuvent être librement partagées et publiées, de sorte que nous n'avons plus besoin de copier les emplacements lors de leur retour.

DelegatingVehicleTracker dans la liste 4.7 n'utilise aucune synchronisation explicite; tout accès à l'état est géré par ConcurrentHashMap , et toutes les clés et valeurs de la carte sont immuables.

liste 4.7. Délégation de la sécurité du Thread à un concurrent.

  public class DelegatingVehicleTracker {

  private final ConcurrentMap<String, Point> locations;
    private final Map<String, Point> unmodifiableMap;

  public DelegatingVehicleTracker(Map<String, Point> points) {
        this.locations = new ConcurrentHashMap<String, Point>(points);
        this.unmodifiableMap = Collections.unmodifiableMap(locations);
    }

  public Map<String, Point> getLocations(){
        return this.unmodifiableMap; // User cannot update point(x,y) as Point is immutable
    }

  public Point getLocation(String id) {
        return locations.get(id);
    }

  public void setLocation(String id, int x, int y) {
        if(locations.replace(id, new Point(x, y)) == null) {
             throw new IllegalArgumentException("invalid vehicle name: " + id);
        }
    }

}

si nous avions utilisé la classe originale MutablePoint au lieu de Point, nous aurions brisé l'encapsulation en laissant getLocations publier une référence à l'état mutable qui n'est pas sûr du fil. Notez que nous avons légèrement modifié le comportement de la classe vehicle tracker; alors que la version monitor renvoie un instantané des emplacements, la version delegating renvoie une vue inamovible mais "live" des emplacements du véhicule. Cela signifie que si thread A appelle getLocations et le thread B modifie plus tard l'emplacement de certains des points, ces changements sont reflétés dans la carte retournée au thread A.

4.3.2. Variables D'État Indépendantes

nous pouvons aussi déléguer la sécurité du thread à plus d'une variable d'état sous-jacente tant que ces variables d'état sous-jacentes sont indépendantes, ce qui signifie que la classe composite n'impose pas d'invariants impliquant les variables d'état multiples.

VisualComponent dans la liste 4.9 est un composant graphique qui permet aux clients d'enregistrer les écouteurs pour les événements de souris et de frappe. Il maintient une liste des auditeurs de chaque type, de sorte que lorsqu'un événement se produit, les écouteurs appropriés peuvent être invoquées. Mais il n'y a pas de relation entre l'ensemble des écouteurs de souris et les écouteurs clés; les deux sont indépendants, et donc VisualComponent peut déléguer ses obligations de sécurité de fil à deux listes sous-jacentes de fil-safe.

liste 4.9. Déléguer la sécurité du Thread à plusieurs Variables D'État sous-jacentes.

public class VisualComponent {
    private final List<KeyListener> keyListeners 
                                        = new CopyOnWriteArrayList<KeyListener>();
    private final List<MouseListener> mouseListeners 
                                        = new CopyOnWriteArrayList<MouseListener>();

  public void addKeyListener(KeyListener listener) {
        keyListeners.add(listener);
    }

  public void addMouseListener(MouseListener listener) {
        mouseListeners.add(listener);
    }

  public void removeKeyListener(KeyListener listener) {
        keyListeners.remove(listener);
    }

  public void removeMouseListener(MouseListener listener) {
        mouseListeners.remove(listener);
    }

}

VisualComponent utilise un CopyOnWriteArrayList pour stocker chaque liste d'auditeurs; il s'agit d'une mise en œuvre de liste sans fil particulièrement adaptée à la gestion des listes d'auditeurs (voir Section 5.2.3). Chaque liste est thread-safe, et parce qu'il n'y a pas de contraintes couplant l'état de l'un à l'état de l'autre, VisualComponent peut déléguer ses responsabilités de sécurité de fil aux objets sous-jacents mouseListeners et keyListeners .

4.3.3. Lorsque La Délégation Échoue

la plupart des classes composites ne sont pas aussi simples que VisualComponent : elles ont des invariants qui relient leurs variables d'état composant. NumberRange dans le Listing 4.10 utilise deux AtomicIntegers pour gérer son état, mais impose une contrainte supplémentaire-que le premier numéro, être inférieure ou égale à la seconde.

liste 4.10. Nombre classe qui ne protège pas suffisamment son Invariant. Ne pas le faire.

public class NumberRange {

  // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

  public void setLower(int i) {
        //Warning - unsafe check-then-act
        if(i > upper.get()) {
            throw new IllegalArgumentException(
                    "Can't set lower to " + i + " > upper ");
        }
        lower.set(i);
    }

  public void setUpper(int i) {
        //Warning - unsafe check-then-act
        if(i < lower.get()) {
            throw new IllegalArgumentException(
                    "Can't set upper to " + i + " < lower ");
        }
        upper.set(i);
    }

  public boolean isInRange(int i){
        return (i >= lower.get() && i <= upper.get());
    }

}

NumberRange is not thread-safe ; il ne préserve pas l'invariant qui contraint inférieur et supérieur. Les méthodes setLower et setUpper tentent de respecter cet invariant, mais le font mal. Les séquences setLower et setUpper sont toutes deux des séquences " check-then-act", mais elles ne sont pas suffisamment verrouillées pour les rendre atomiques. Si la plage de nombre tient (0, 10), et un thread appelle setLower(5) alors qu'un autre thread appelle setUpper(4) , avec un timing malchanceux les deux passeront les vérifications dans les setters et les deux modifications seront appliquées. Le résultat est que la plage tient maintenant (5, 4) - un état invalide . Ainsi alors que les AtomicInteger sous-jacents sont thread-safe, la classe composite n'est pas . Parce que les variables d'état sous-jacentes lower et upper ne sont pas indépendantes, NumberRange ne peut pas simplement déléguez la sécurité du thread à ses variables d'état thread-safe.

NumberRange peut être rendu thread-safe en utilisant le verrouillage pour maintenir ses invariants, tels que la garde inférieure et supérieure avec une serrure commune. Il doit également éviter de publier inférieur et supérieur pour empêcher les clients de subvertir ses invariants.

si une classe comporte des actions composées, comme le fait NumberRange , la délégation à elle seule n'est pas non plus une approche appropriée pour la sécurité des fils. Dans ces cas, la classe doit fournir son propre verrouillage pour s'assurer que les actions composées sont atomiques, à moins que l'action composée entière puisse également être déléguée aux variables d'état sous-jacentes.

si une classe est composée de plusieurs variables indépendantes d'état thread-safe et n'a pas d'opérations qui ont des transitions d'état invalides, alors elle peut déléguer la sécurité du thread aux variables d'état sous-jacentes.

0
répondu overexchange 2017-10-11 17:50:27