162 Stimmen

Was bedeutet "threadsicher"?

Kürzlich habe ich versucht, von einem anderen Thread (als dem UI-Thread) auf ein Textfeld zuzugreifen, und es wurde eine Ausnahme ausgelöst. Es sagte etwas über den "Code nicht Thread sicher" und so endete ich schriftlich einen Delegaten (Beispiel aus MSDN half) und rufen Sie es stattdessen.

Aber selbst dann habe ich nicht ganz verstanden, warum der ganze zusätzliche Code notwendig war.

Aktualisieren: Werde ich auf ernsthafte Probleme stoßen, wenn ich die

Controls.CheckForIllegalCrossThread..blah =true

5 Stimmen

Normalerweise bedeutet "thread safe", was immer die Person, die den Begriff verwendet, denkt, dass es bedeutet, zumindest für diese Person. Als solches ist es kein sehr nützliches Sprachkonstrukt - man muss viel, viel spezifischer sein, wenn man über das Verhalten von threaded Code spricht.

8 Stimmen

0 Stimmen

@dave Sorry, ich habe versucht zu suchen, aber aufgegeben...trotzdem danke...

1voto

Aaron Robson Punkte 139

Ich finde das Konzept der http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 was ich normalerweise als unsicheres Threading bezeichne, nämlich wenn eine Methode einen Nebeneffekt wie eine globale Variable hat und sich darauf verlässt.

Ich habe zum Beispiel Code gesehen, der Fließkommazahlen in eine Zeichenkette formatiert. Wenn zwei davon in verschiedenen Threads ausgeführt werden, kann der globale Wert von decimalSeparator dauerhaft in '.' geändert werden.

//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

-4voto

overexchange Punkte 13522

Zum Verständnis der Fadensicherheit lesen Sie bitte unten Abschnitte :

4.3.1. Beispiel: Fahrzeugverfolgung durch Delegation

Als ausführlicheres Beispiel für die Delegation wollen wir eine Version des Fahrzeugverfolgers konstruieren, die an eine thread-sichere Klasse delegiert. Wir speichern die Standorte in einer Map, also beginnen wir mit einer thread-sicheren Map-Implementierung, ConcurrentHashMap . Außerdem speichern wir den Ort mit einer unveränderlichen Point-Klasse anstelle von MutablePoint wie in Listing 4.6 gezeigt.

Listing 4.6. Unveränderliche Punktklasse, die von DelegatingVehicleTracker verwendet wird.

 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 ist thread-sicher, weil es unveränderlich ist. Unveränderliche Werte können frei freigegeben und veröffentlicht werden, so dass wir die Speicherorte bei der Rückgabe nicht mehr kopieren müssen.

DelegatingVehicleTracker in Listing 4.7 verwendet keine explizite Synchronisierung; der gesamte Zugriff auf den Zustand wird von ConcurrentHashMap und alle Schlüssel und Werte der Map sind unveränderlich.

Auflistung 4.7. Delegieren der Thread-Sicherheit an eine ConcurrentHashMap.

  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);
        }
    }

}

Wenn wir das Original verwendet hätten MutablePoint Klasse anstelle von Point, würden wir die Kapselung brechen, indem wir die getLocations einen Verweis auf einen veränderlichen Zustand veröffentlichen, der nicht thread-sicher ist. Beachten Sie, dass wir das Verhalten der Fahrzeugverfolgungsklasse geringfügig geändert haben; während die Monitorversion einen Schnappschuss der Standorte zurückgibt, gibt die delegierende Version eine unveränderbare, aber "Live"-Ansicht der Fahrzeugstandorte zurück. Das bedeutet, dass wenn Thread A die getLocations und Thread B später die Position einiger Punkte ändert, werden diese Änderungen in der an Thread A zurückgegebenen Map berücksichtigt.

4.3.2. Unabhängige Zustandsvariablen

Wir können die Fadensicherheit auch an mehr als eine zugrundeliegende Zustandsvariable delegieren, solange diese zugrundeliegenden Zustandsvariablen unabhängig sind, was bedeutet, dass die zusammengesetzte Klasse keine Invarianten für die mehreren Zustandsvariablen vorschreibt.

VisualComponent in Listing 4.9 ist eine grafische Komponente, die es den Clients ermöglicht, Listener für Maus- und Tastendruckereignisse zu registrieren. Sie verwaltet eine Liste der registrierten Listener für jeden Typ, so dass beim Auftreten eines Ereignisses die entsprechenden Listener aufgerufen werden können. Es besteht jedoch keine Beziehung zwischen den Maus- und den Tasten-Listenern; die beiden sind unabhängig voneinander, und daher VisualComponent kann seine Verpflichtungen bezüglich der Thread-Sicherheit an zwei zugrunde liegende Thread-sichere Listen delegieren.

Auflistung 4.9. Delegieren der Thread-Sicherheit an mehrere zugrundeliegende Zustandsvariablen.

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 verwendet eine CopyOnWriteArrayList um jede Hörerliste zu speichern; dies ist eine thread-sichere List-Implementierung, die sich besonders für die Verwaltung von Hörerlisten eignet (siehe Abschnitt 5.2.3). Jede Liste ist thread-sicher, und da es keine Beschränkungen gibt, die den Zustand der einen mit dem der anderen koppeln, VisualComponent kann seine Verantwortlichkeiten für die Threadsicherheit an die zugrunde liegende mouseListeners y keyListeners objets.

4.3.3. Wenn die Delegation scheitert

Die meisten zusammengesetzten Klassen sind nicht so einfach wie VisualComponent : Sie haben Invarianten, die ihre Komponenten-Zustandsvariablen in Beziehung setzen. NumberRange in Listing 4.10 verwendet zwei AtomicIntegers um seinen Zustand zu verwalten, legt aber eine zusätzliche Bedingung fest, nämlich dass die erste Zahl kleiner oder gleich der zweiten sein muss.

Listing 4.10. Zahlenbereichsklasse, die ihre Invarianten nicht hinreichend schützt. Tun Sie das nicht.

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 es nicht thread-sicher ; sie bewahrt nicht die Invariante, die das untere und obere Ende einschränkt. Die setLower y setUpper Methoden versuchen, diese Invariante zu respektieren, tun dies aber nur unzureichend. Beide setLower y setUpper sind "check-then-act"-Sequenzen, aber sie verwenden nicht genügend Sperren, um sie atomar zu machen. Wenn der Zahlenbereich (0, 10) gilt, und ein Thread ruft setLower(5) während ein anderer Thread die setUpper(4) Mit etwas unglücklichem Timing werden beide die Prüfungen in den Setzern bestehen und beide Änderungen werden angewendet. Das Ergebnis ist, dass der Bereich nun (5, 4)- einen ungültigen Zustand . Also Während die zugrunde liegenden AtomicIntegers thread-sicher sind, ist die zusammengesetzte Klasse nicht thread-sicher. . Da die zugrunde liegenden Zustandsvariablen lower y upper sind nicht unabhängig, NumberRange kann die Thread-Sicherheit nicht einfach an seine Thread-sicheren Zustandsvariablen delegieren.

NumberRange könnte thread-sicher gemacht werden, indem Sperren verwendet werden, um die Invarianten zu erhalten, wie z.B. das Sichern von lower und upper mit einer gemeinsamen Sperre. Es muss auch vermeiden, lower und upper zu veröffentlichen, um zu verhindern, dass Clients seine Invarianten unterwandern können.

Wenn eine Klasse zusammengesetzte Aktionen hat, wie NumberRange tut, ist die Delegation allein wiederum kein geeigneter Ansatz für die Fadensicherheit. In diesen Fällen muss die Klasse ihre eigenen Sperren bereitstellen, um sicherzustellen, dass zusammengesetzte Aktionen atomar sind, es sei denn, die gesamte zusammengesetzte Aktion kann auch an die zugrunde liegenden Zustandsvariablen delegiert werden.

Wenn eine Klasse aus mehreren unabhängigen thread-sicheren Zustandsvariablen besteht und keine Operationen mit ungültigen Zustandsübergängen hat, kann sie die Thread-Sicherheit an die zugrunde liegenden Zustandsvariablen delegieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X