it-swarm.com.de

Was versteht man unter "threadsicherem" Code?

Bedeutet dies, dass zwei Threads die zugrunde liegenden Daten nicht gleichzeitig ändern können? Oder bedeutet dies, dass das angegebene Codesegment mit vorhersehbaren Ergebnissen ausgeführt wird, wenn es von mehr als einem Thread ausgeführt wird?

334
Varun Mahajan

Aus Wikipedia:

Thread-Sicherheit ist ein Computerprogrammierungskonzept, das im Kontext von Multithread-Programmen anwendbar ist. Ein Codeteil ist threadsicher, wenn es bei der gleichzeitigen Ausführung durch mehrere Threads ordnungsgemäß funktioniert. Insbesondere muss die Anforderung erfüllt sein, dass mehrere Threads auf dieselben gemeinsam genutzten Daten zugreifen und dass jeweils nur ein Thread auf ein gemeinsam genutztes Datenelement zugreifen darf.

Es gibt einige Möglichkeiten, um die Thread-Sicherheit zu erreichen:

Wiedereintritt:

Code so schreiben, dass er teilweise von einer Task ausgeführt, von einer anderen erneut eingegeben und dann von der ursprünglichen Task fortgesetzt werden kann. Dies erfordert das Speichern von Statusinformationen in Variablen, die für jede Aufgabe lokal sind, normalerweise auf ihrem Stapel, anstatt in statischen oder globalen Variablen.

Gegenseitiger Ausschluss:

Der Zugriff auf gemeinsam genutzte Daten wird mithilfe von Mechanismen serialisiert, die sicherstellen, dass immer nur ein Thread die gemeinsam genutzten Daten liest oder schreibt. Wenn ein Teil des Codes auf mehrere gemeinsam genutzte Datenteile zugreift, ist große Sorgfalt erforderlich. Zu den Problemen gehören die Rennbedingungen, Deadlocks, Livelocks, Hunger und verschiedene andere Probleme, die in vielen Lehrbüchern für Betriebssysteme aufgeführt sind.

Thread-lokaler Speicher:

Variablen werden so lokalisiert, dass jeder Thread eine eigene private Kopie hat. Diese Variablen behalten ihre Werte über Unterprogramme und andere Codegrenzen hinweg bei und sind threadsicher, da sie für jeden Thread lokal sind, obwohl der Code, der auf sie zugreift, möglicherweise wiedereintrittsfähig ist.

Atomare Operationen:

Auf freigegebene Daten wird mit atomaren Operationen zugegriffen, die nicht von anderen Threads unterbrochen werden können. Dies erfordert normalerweise die Verwendung spezieller Anweisungen in Maschinensprache, die möglicherweise in einer Laufzeitbibliothek verfügbar sind. Da die Operationen atomar sind, werden die gemeinsam genutzten Daten immer in einem gültigen Zustand gehalten, unabhängig davon, auf welche anderen Threads zugegriffen wird. Atomare Operationen bilden die Grundlage vieler Gewindesperrmechanismen.

lies mehr:

http://en.wikipedia.org/wiki/Thread_safety


248
Blauohr

Thread-sicherer Code ist Code, der auch dann funktioniert, wenn viele Threads ihn gleichzeitig ausführen.

http://mindprod.com/jgloss/threadsafe.html

75
Marek Blotny

Eine informativere Frage ist, warum Code nicht threadsicher ist - und die Antwort lautet, dass es vier Bedingungen gibt, die zutreffen müssen ... Stellen Sie sich den folgenden Code vor (und dessen maschinelle Übersetzung)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. Die erste Bedingung ist, dass es Speicherorte gibt, auf die von mehr als einem Thread aus zugegriffen werden kann. In der Regel handelt es sich bei diesen Speicherorten um globale/statische Variablen oder um Heap-Speicher, der über globale/statische Variablen erreichbar ist. Jeder Thread erhält einen eigenen Stapelrahmen für lokale Variablen mit Funktions-/Methodenbereich, sodass auf diese lokalen Funktions-/Methodenvariablen (otoh) (die sich auf dem Stapel befinden) nur von dem einen Thread aus zugegriffen werden kann, der diesen Stapel besitzt.
  2. Die zweite Bedingung ist, dass es eine Eigenschaft gibt (oft invariant), die diesen gemeinsam genutzten Speicherorten zugeordnet ist und die wahr oder gültig sein muss, damit das Programm ordnungsgemäß funktioniert. Im obigen Beispiel lautet die Eigenschaft " totalRequests muss genau angeben, wie oft ein Thread einen Teil der increment-Anweisung ausgeführt hat". In der Regel muss diese invariante Eigenschaft den Wert true haben (in diesem Fall muss totalRequests eine genaue Anzahl haben), bevor eine Aktualisierung durchgeführt wird, damit die Aktualisierung korrekt ist.
  3. Die dritte Bedingung ist, dass die invariante Eigenschaft während eines Teils der tatsächlichen Aktualisierung NICHT gültig ist. (Es ist vorübergehend ungültig oder falsch während eines Teils der Verarbeitung). In diesem speziellen Fall erfüllt totalRequests ab dem Zeitpunkt, zu dem totalRequests abgerufen wird, bis zu dem Zeitpunkt, zu dem der aktualisierte Wert gespeichert wird, die Invariante not.
  4. Die vierte und letzte Bedingung, die auftreten muss, damit ein Rennen stattfinden kann (und damit der Code [~ # ~] nicht [~ # ~] "thread" ist -safe ") bedeutet, dass ein anderer Thread auf den gemeinsamen Speicher zugreifen kann while die Invariante ist defekt, was zu inkonsistentem oder falschem Verhalten führt.
48
Charles Bretana

Ich mag die Definition aus Brian Goetz 'Java Concurrency in Practice wegen ihrer Vollständigkeit

"Eine Klasse ist threadsicher, wenn sie sich beim Zugriff von mehreren Threads aus korrekt verhält, unabhängig von der Planung oder Verschachtelung der Ausführung dieser Threads durch die Laufzeitumgebung und ohne zusätzliche Synchronisation oder sonstige Koordination durch den aufrufenden Code. "

32
Buu Nguyen

Wie bereits erwähnt, bedeutet Thread-Sicherheit, dass ein Teil des Codes fehlerfrei funktioniert, wenn er von mehreren Threads gleichzeitig verwendet wird.

Es sollte beachtet werden, dass dies manchmal mit einem Preis verbunden ist, der Computerzeit und einer komplexeren Codierung, sodass dies nicht immer wünschenswert ist. Wenn eine Klasse sicher für nur einen Thread verwendet werden kann, ist dies möglicherweise besser.

Beispiel: Java hat zwei Klassen, die fast gleichwertig sind, StringBuffer und StringBuilder. Der Unterschied besteht darin, dass StringBuffer threadsicher ist. Daher kann eine einzelne Instanz eines StringBuffer von mehreren Threads gleichzeitig verwendet werden. StringBuilder ist nicht threadsicher und wurde als leistungsstärkerer Ersatz für diese Fälle entwickelt (die überwiegende Mehrheit). wenn der String nur aus einem Thread besteht.

26
Marcus Downing

Thread-Safe-Code funktioniert wie angegeben, auch wenn er von verschiedenen Threads gleichzeitig eingegeben wird. Dies bedeutet häufig, dass interne Datenstrukturen oder Vorgänge, die ununterbrochen ablaufen sollen, gleichzeitig gegen unterschiedliche Änderungen geschützt sind.

22
Mnementh

Ein einfacherer Weg, dies zu verstehen, ist, dass Code nicht threadsicher ist. Es gibt zwei Hauptprobleme, die dazu führen, dass eine Thread-Anwendung unerwünschtes Verhalten zeigt.

  • Zugriff auf gemeinsam genutzte Variablen ohne Sperren
    Diese Variable kann während der Ausführung der Funktion von einem anderen Thread geändert werden. Sie möchten es mit einem Sperrmechanismus verhindern, um das Verhalten Ihrer Funktion sicher zu stellen. Als Faustregel gilt, das Schloss so kurz wie möglich zu halten.

  • Deadlock durch gegenseitige Abhängigkeit von gemeinsam genutzten Variablen
    Wenn Sie zwei gemeinsam genutzte Variablen A und B haben, sperren Sie in einer Funktion zuerst A und später B. In einer anderen Funktion beginnen Sie, B zu sperren, und nach einer Weile sperren Sie A. Dies ist ein Potenzial Deadlock, bei dem die erste Funktion darauf wartet, dass B entsperrt wird, während die zweite Funktion darauf wartet, dass A entsperrt wird. Dieses Problem tritt wahrscheinlich nicht in Ihrer Entwicklungsumgebung und nur von Zeit zu Zeit auf. Um dies zu vermeiden, müssen alle Sperren immer in derselben Reihenfolge sein.

20
Hapkido

Ja und nein.

Thread-Sicherheit ist ein bisschen mehr als nur sicherzustellen, dass jeweils nur ein Thread auf Ihre freigegebenen Daten zugreift. Sie müssen den sequentiellen Zugriff auf gemeinsam genutzte Daten sicherstellen und gleichzeitig Rennbedingungen , Deadlocks , Livelocks und Ressourcen) vermeiden Hunger .

Unvorhersehbare Ergebnisse, wenn mehrere Threads ausgeführt werden, sind keine erforderliche Bedingung für thread-sicheren Code, es handelt sich jedoch häufig um ein Nebenprodukt. Beispielsweise könnte ein Producer-Consumer - Schema mit einer gemeinsam genutzten Warteschlange, einem Producer-Thread und wenigen Consumer-Threads eingerichtet werden, und der Datenfluss könnte perfekt vorhersehbar sein. Wenn Sie mehr Verbraucher einführen, sehen Sie eher zufällig aussehende Ergebnisse.

9
Bill the Lizard

Einfach - Code läuft einwandfrei, wenn viele Threads diesen Code gleichzeitig ausführen.

8
Greg Balajewicz

Im Wesentlichen können in einer Umgebung mit mehreren Threads viele Probleme auftreten (Neuanordnung von Befehlen, teilweise konstruierte Objekte, gleiche Variable mit unterschiedlichen Werten in verschiedenen Threads aufgrund von Caching auf CPU-Ebene usw.).

Mir gefällt die Definition von Java Concurrency in Practice :

Ein [Teil des Codes] ist threadsicher, wenn er sich beim Zugriff von mehreren Threads aus korrekt verhält, unabhängig von der Planung oder Verschachtelung der Ausführung dieser Threads durch die Laufzeitumgebung und ohne zusätzliche Synchronisation oder sonstige Koordination seitens des Code aufrufen.

Mit richtig ist gemeint, dass sich das Programm in Übereinstimmung mit seinen Spezifikationen verhält.

Erfundenes Beispiel

Stellen Sie sich vor, Sie implementieren einen Zähler. Man könnte sagen, dass es sich korrekt verhält, wenn:

  • counter.next() gibt niemals einen Wert zurück, der bereits zuvor zurückgegeben wurde (der Einfachheit halber nehmen wir keinen Überlauf usw. an)
  • alle Werte von 0 bis zum aktuellen Wert wurden zu einem bestimmten Zeitpunkt zurückgegeben (kein Wert wird übersprungen).

Ein threadsicherer Leistungsindikator verhält sich gemäß diesen Regeln unabhängig davon, wie viele Threads gleichzeitig darauf zugreifen (was bei einer naiven Implementierung normalerweise nicht der Fall ist).

Hinweis: Crosspost bei Programmierern

8
assylias

Ich möchte einige weitere Informationen zu anderen guten Antworten hinzufügen.

Thread-Sicherheit impliziert, dass mehrere Threads Daten in dasselbe Objekt schreiben/lesen können, ohne dass Speicherinkonsistenzfehler auftreten. In einem Programm mit vielen Threads verursacht ein threadsicheres Programm keine Nebenwirkungen für gemeinsam genutzte Daten.

Schauen Sie sich diese SE-Frage für weitere Details an:

Was bedeutet threadsicher?

Thread-sicheres Programm garantiert Speicherkonsistenz.

Aus der Oracle-Dokumentation Seite über die erweiterte gleichzeitige API:

Eigenschaften der Speicherkonsistenz:

In Kapitel 17 der Java ™ -Sprachenspezifikation wird die Vor-Zwischen-Beziehung für Speicheroperationen wie Lesen und Schreiben von gemeinsam genutzten Variablen definiert. Die Ergebnisse eines Schreibvorgangs eines Threads sind für einen Lesevorgang eines anderen Threads garantiert nur sichtbar, wenn der Schreibvorgang vor dem Lesevorgang erfolgt.

Die Konstrukte synchronized und volatile sowie die Methoden Thread.start() und Thread.join() können happen-before Bilden = Beziehungen.

Die Methoden aller Klassen in Java.util.concurrent Und ihren Unterpaketen erweitern diese Garantien auf Synchronisation auf höherer Ebene. Insbesondere:

  1. Aktionen in einem Thread vor dem Platzieren eines Objekts in einer gleichzeitigen Auflistung werden ausgeführt, bevor Aktionen nach dem Zugriff auf oder dem Entfernen dieses Elements aus der Auflistung in einem anderen Thread ausgeführt werden.
  2. Aktionen in einem Thread, bevor ein Runnable an ein Executor übergeben wird, werden ausgeführt, bevor die Ausführung beginnt. Ähnliches gilt für Callables, die an ExecutorService gesendet wurden.
  3. Aktionen, die von der durch Future dargestellten asynchronen Berechnung ausgeführt werden, geschehen vor Aktionen, nachdem das Ergebnis über Future.get() in einem anderen Thread abgerufen wurde.
  4. Aktionen vor dem "Freigeben" Synchronizer Methoden wie Lock.unlock, Semaphore.release, and CountDownLatch.countDown Werden ausgeführt, bevor eine erfolgreiche "Erfassungsmethode" wie Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await Für dasselbe Synchronizerobjekt ausgeführt wird in einem anderen Thread.
  5. Für jedes Thread-Paar, das Objekte über ein Exchanger erfolgreich austauscht, werden in jedem Thread Aktionen vor dem exchange() ausgeführt, die auf den entsprechenden exchange () in einem anderen Thread folgen.
  6. Aktionen vor dem Aufruf von CyclicBarrier.await Und Phaser.awaitAdvance (Sowie seiner Varianten) werden ausgeführt, bevor Aktionen von der Barriere-Aktion ausgeführt werden, und Aktionen, die von der Barriere-Aktion ausgeführt werden, werden ausgeführt, bevor Aktionen nach einer erfolgreichen Rückkehr ausgeführt werden aus dem entsprechenden warten in anderen threads.
5
Ravindra babu

Verwechseln Sie Thread-Sicherheit nicht mit Determinismus. Thread-sicherer Code kann auch nicht deterministisch sein. Angesichts der Schwierigkeit, Probleme mit Thread-Code zu debuggen, ist dies wahrscheinlich der Normalfall. :-)

Die Thread-Sicherheit stellt lediglich sicher, dass kein anderer Thread auf diese Weise auf die Daten zugreifen kann, wenn ein Thread freigegebene Daten ändert oder liest. Wenn Ihr Code von einer bestimmten Ausführungsreihenfolge abhängt, um die Richtigkeit zu gewährleisten, benötigen Sie andere Synchronisationsmechanismen, die über die für die Threadsicherheit erforderlichen Mechanismen hinausgehen.

4
tvanfosson

So vervollständigen Sie andere Antworten:

Die Synchronisierung ist nur dann problematisch, wenn der Code in Ihrer Methode eine der beiden folgenden Aktionen ausführt:

  1. funktioniert mit externen Ressourcen, die nicht threadsicher sind.
  2. Liest oder ändert ein persistentes Objekt oder Klassenfeld

Dies bedeutet, dass in Ihrer Methode definierte Variablen immer threadsicher sind. Jeder Aufruf einer Methode hat eine eigene Version dieser Variablen. Wenn die Methode von einem anderen Thread oder demselben Thread aufgerufen wird oder selbst wenn die Methode sich selbst aufruft (Rekursion), werden die Werte dieser Variablen nicht gemeinsam genutzt.

Es wird nicht garantiert, dass die Thread-Planung Round-Robin ist. Eine Aufgabe kann die CPU auf Kosten von Threads mit der gleichen Priorität vollständig auslasten. Sie können Thread.yield () verwenden, um ein Gewissen zu haben. Sie können (in Java) Thread.setPriority (Thread.NORM_PRIORITY-1) verwenden, um die Priorität eines Threads zu verringern

Achten Sie außerdem auf:

  • die hohen Laufzeitkosten (von anderen bereits erwähnt) für Anwendungen, die über diese "thread-sicheren" Strukturen iterieren.
  • Thread.sleep (5000) soll 5 Sekunden schlafen. Wenn jedoch jemand die Systemzeit ändert, können Sie sehr lange oder gar nicht schlafen. Das Betriebssystem zeichnet die Weckzeit in absoluter und nicht in relativer Form auf.
3
VonC

Ja und ja. Dies bedeutet, dass Daten nicht von mehr als einem Thread gleichzeitig geändert werden. Ihr Programm funktioniert jedoch möglicherweise wie erwartet und scheint thread-sicher zu sein, auch wenn dies grundsätzlich nicht der Fall ist.

Beachten Sie, dass die Unvorhersehbarkeit der Ergebnisse eine Folge von „Rennbedingungen“ ist, die wahrscheinlich dazu führen, dass Daten in einer anderen Reihenfolge als der erwarteten geändert werden.

1
Steve Knight

Lassen Sie uns dies anhand eines Beispiels beantworten:

class NonThreadSafe {

    private int counter = 0;

    public boolean countTo10() {
        count = count + 1;
        return (count == 10);
    }

Die Methode countTo10 Fügt dem Zähler eins hinzu und gibt dann true zurück, wenn der Zählwert 10 erreicht hat. Sie sollte nur einmal true zurückgeben.

Dies funktioniert, solange nur ein Thread den Code ausführt. Wenn zwei Threads den Code gleichzeitig ausführen, können verschiedene Probleme auftreten.

Wenn die Zählung beispielsweise mit 9 beginnt, kann ein Thread 1 zur Zählung hinzufügen (10 machen), aber dann kann ein zweiter Thread in die Methode eintreten und erneut 1 hinzufügen (11 machen), bevor der erste Thread die Chance hat, den Vergleich mit 10 durchzuführen Dann führen beide Threads den Vergleich durch und stellen fest, dass count 11 ist und keines true zurückgibt.

Dieser Code ist also nicht threadsicher.

Im Wesentlichen werden alle Multithreading-Probleme durch eine Variation dieser Art von Problem verursacht.

Die Lösung besteht darin, sicherzustellen, dass die Addition und der Vergleich nicht getrennt werden können (z. B. indem die beiden Anweisungen mit einer Art Synchronisationscode umgeben werden) oder indem eine Lösung entwickelt wird, die keine zwei Operationen erfordert. Ein solcher Code wäre threadsicher.

0
rghome

In einfachsten Worten: P Wenn es sicher ist, mehrere Threads in einem Codeblock auszuführen, ist es threadsicher *

*Bedingungen gelten

Bedingungen werden von anderen Antworten wie 1 erwähnt. Das Ergebnis sollte dasselbe sein, wenn Sie einen Thread oder mehrere Threads darüber ausführen usw.

0
shabby