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 mehrere Threads dieses Codesegment ausführen?
multithreading
language-agnostic
programming-languages
concurrency
Varun Mahajan
quelle
quelle
Antworten:
Aus Wikipedia:
Weiterlesen:
http://en.wikipedia.org/wiki/Thread_safety
auf Deutsch: http://de.wikipedia.org/wiki/Threadsicherheit
auf Französisch: http://fr.wikipedia.org/wiki/Threadsafe (sehr kurz)
quelle
Thread-sicherer Code ist Code, der auch dann funktioniert, wenn viele Threads ihn gleichzeitig ausführen.
http://mindprod.com/jgloss/threadsafe.html
quelle
Eine informativere Frage ist, was Code nicht threadsicher macht - und die Antwort lautet, dass vier Bedingungen erfüllt sein müssen ... Stellen Sie sich den folgenden Code vor (und es handelt sich um eine maschinelle Sprachübersetzung).
quelle
Ich mag die Definition aus Brian Goetz 'Java Concurrency in Practice für ihre Vollständigkeit
"Eine Klasse ist threadsicher, wenn sie sich beim Zugriff von mehreren Threads korrekt verhält, unabhängig von der Planung oder Verschachtelung der Ausführung dieser Threads durch die Laufzeitumgebung und ohne zusätzliche Synchronisation oder andere Koordination seitens des aufrufenden Codes. ""
quelle
Wie andere bereits betont haben, bedeutet Thread-Sicherheit, dass ein Code fehlerfrei funktioniert, wenn er von mehr als einem Thread gleichzeitig verwendet wird.
Es ist erwähnenswert, dass dies manchmal mit Kosten für Computerzeit und komplexerer Codierung verbunden ist, sodass dies nicht immer wünschenswert ist. Wenn eine Klasse nur für einen Thread sicher verwendet werden kann, ist dies möglicherweise besser.
Zum Beispiel hat Java zwei Klassen, die fast gleichwertig sind,
StringBuffer
undStringBuilder
. Der Unterschied besteht darin, dassStringBuffer
Thread-sicher ist, sodass eine einzelne Instanz von aStringBuffer
von mehreren Threads gleichzeitig verwendet werden kann.StringBuilder
ist nicht threadsicher und wurde als leistungsstärkerer Ersatz für die Fälle (die überwiegende Mehrheit) entwickelt, in denen der String nur von einem Thread erstellt wird.quelle
Thread-Safe-Code funktioniert wie angegeben, auch wenn er gleichzeitig von verschiedenen Threads eingegeben wird. Dies bedeutet häufig, dass interne Datenstrukturen oder Vorgänge, die ohne Unterbrechung ausgeführt werden sollen, gleichzeitig vor unterschiedlichen Änderungen geschützt sind.
quelle
Ein einfacher Weg, es zu verstehen, ist, was Code nicht threadsicher macht. Es gibt zwei Hauptprobleme, die dazu führen, dass eine Thread-Anwendung unerwünschtes Verhalten aufweist.
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 dies mit einem Verriegelungsmechanismus verhindern, um das Verhalten Ihrer Funktion sicherzustellen. Als Faustregel gilt, dass das Schloss so schnell wie möglich aufbewahrt wird.
Deadlock durch gegenseitige Abhängigkeit von gemeinsam genutzten Variablen
Wenn Sie zwei gemeinsam genutzte Variablen A und B haben. In einer Funktion sperren Sie zuerst A und später B. In einer anderen Funktion sperren Sie B und nach einer Weile sperren Sie A. Dies ist ein möglicher 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 Schlösser immer in derselben Reihenfolge sein.
quelle
Ja und nein.
Die Thread-Sicherheit ist ein bisschen mehr als nur sicherzustellen, dass nur ein Thread gleichzeitig auf Ihre freigegebenen Daten zugreift. Sie müssen den sequentiellen Zugriff auf gemeinsam genutzte Daten sicherstellen und gleichzeitig Rennbedingungen , Deadlocks , Livelocks und Ressourcenmangel .
Unvorhersehbare Ergebnisse, wenn mehrere Threads ausgeführt werden, sind keine erforderliche Bedingung für threadsicheren Code, aber häufig 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 ist möglicherweise perfekt vorhersehbar. Wenn Sie mehr Verbraucher vorstellen, sehen Sie mehr zufällig aussehende Ergebnisse.
quelle
Im Wesentlichen können in einer Umgebung mit mehreren Threads viele Dinge schief gehen (Neuordnung von Anweisungen, teilweise konstruierte Objekte, dieselbe Variable mit unterschiedlichen Werten in unterschiedlichen Threads aufgrund von Caching auf CPU-Ebene usw.).
Ich mag die Definition von Java Concurrency in der Praxis :
Mit richtig meinen sie, 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 richtig verhält, wenn:
counter.next()
Gibt niemals einen Wert zurück, der bereits zuvor zurückgegeben wurde (der Einfachheit halber wird kein Überlauf usw. angenommen).Ein thread-sicherer Zähler würde sich gemäß diesen Regeln verhalten, unabhängig davon, wie viele Threads gleichzeitig darauf zugreifen (was bei einer naiven Implementierung normalerweise nicht der Fall ist).
Hinweis: Cross-Post auf Programmierern
quelle
Einfach - Code läuft einwandfrei, wenn viele Threads diesen Code gleichzeitig ausführen.
quelle
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 beim Ändern oder Lesen freigegebener Daten auf eine Weise darauf zugreifen kann, die die Daten ändert. Wenn Ihr Code für die Richtigkeit von einer bestimmten Ausführungsreihenfolge abhängt, benötigen Sie andere Synchronisationsmechanismen als die für die Thread-Sicherheit erforderlichen, um dies sicherzustellen.
quelle
Ich möchte zusätzlich zu anderen guten Antworten weitere Informationen hinzufügen.
Thread-Sicherheit bedeutet, dass mehrere Threads Daten in dasselbe Objekt schreiben / lesen können, ohne dass Speicherinkonsistenzfehler auftreten. In einem Programm mit mehreren Multithreads verursacht ein threadsicheres Programm keine Nebenwirkungen auf gemeinsam genutzte Daten .
Schauen Sie sich diese SE-Frage für weitere Details an:
Was bedeutet threadsafe?
Thread-sicheres Programm garantiert Speicherkonsistenz .
Von der Oracle - Dokumentation Seite auf erweiterte gleichzeitige API:
Eigenschaften der Speicherkonsistenz:
Kapitel 17 der Java ™ -Sprachspezifikation definiert die Vor-Vor-Beziehung für Speicheroperationen wie Lesen und Schreiben von gemeinsam genutzten Variablen. Die Ergebnisse eines Schreibvorgangs durch einen Thread sind für einen Lesevorgang durch einen anderen Thread garantiert nur dann sichtbar, wenn der Schreibvorgang vor dem Lesevorgang ausgeführt wird .
Die
synchronized
undvolatile
-Konstrukte sowie dieThread.start()
undThread.join()
-Methoden können Vor-Vor- Beziehungen bilden.Die Methoden aller Klassen in
java.util.concurrent
und ihrer Unterpakete erweitern diese Garantien auf die Synchronisation auf höherer Ebene. Bestimmtes:Runnable
an einExecutor
Ereignis, bevor dessen Ausführung beginnt. Ähnliches gilt für Callables, die an eineExecutorService
.Future
, nachdem das Ergebnis über einenFuture.get()
anderen Thread abgerufen wurde .Lock.unlock, Semaphore.release, and CountDownLatch.countDown
Vorabaktionen nach einer erfolgreichen "Erfassungs" -Methode, z. B.Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
für dasselbe Synchronisierungsobjekt in einem anderen Thread.Exchanger
Aktion vor dem austauschtexchange()
in jedem Thread statt - vor denen nach dem entsprechenden Austausch () in einem anderen Thread.CyclicBarrier.await
undPhaser.awaitAdvance
(sowie deren Varianten) werden ausgeführt, bevor Aktionen ausgeführt werden, die von der Barriereaktion ausgeführt werden, und Aktionen, die von der Barriereaktion ausgeführt werden, bevor Aktionen ausgeführt werden, die auf eine erfolgreiche Rückkehr vom entsprechenden Warten in anderen Threads folgen.quelle
So vervollständigen Sie andere Antworten:
Die Synchronisierung ist nur dann ein Problem, wenn der Code in Ihrer Methode eines von zwei Dingen ausführt:
Dies bedeutet, dass innerhalb 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 vom selben Thread aufgerufen wird oder selbst wenn sich die Methode selbst aufruft (Rekursion), werden die Werte dieser Variablen nicht gemeinsam genutzt.
Die Thread-Planung ist nicht garantiert Round-Robin . Eine Aufgabe kann die CPU auf Kosten von Threads mit derselben Priorität völlig belasten. 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:
quelle
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 erscheint threadsicher, 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 als der erwarteten Reihenfolge geändert werden.
quelle
Beantworten wir dies anhand eines Beispiels:
Die
countTo10
Methode fügt dem Zähler eine hinzu und gibt dann true zurück, wenn die Anzahl 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 beispielsweise die Zählung mit 9 beginnt, kann ein Thread 1 zur Zählung hinzufügen (10 machen), aber dann kann ein zweiter Thread die Methode eingeben und erneut 1 hinzufügen (11 machen), bevor der erste Thread die Chance hat, den Vergleich mit 10 auszuführen Dann führen beide Threads den Vergleich durch und stellen fest, dass count 11 ist und keiner von beiden 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 das Hinzufügen und der Vergleich nicht getrennt werden können (z. B. indem die beiden Anweisungen durch eine Art Synchronisationscode umgeben werden) oder indem eine Lösung entwickelt wird, die keine zwei Operationen erfordert. Ein solcher Code wäre threadsicher.
quelle
Zumindest in C ++ halte ich Thread-Safe für eine Art Fehlbezeichnung, da es viel aus dem Namen herauslässt. Um threadsicher zu sein, muss Code normalerweise proaktiv sein. Es ist im Allgemeinen keine passive Qualität.
Damit eine Klasse profilsicher ist, muss sie über "zusätzliche" Funktionen verfügen, die zusätzlichen Aufwand verursachen. Diese Funktionen sind Teil der Implementierung der Klasse und im Allgemeinen vor der Schnittstelle verborgen. Das heißt, verschiedene Threads können auf jedes Mitglied der Klasse zugreifen, ohne sich jemals Gedanken über Konflikte mit einem gleichzeitigen Zugriff eines anderen Threads machen zu müssen, UND dies auf sehr faule Weise unter Verwendung eines einfachen alten normalen menschlichen Codierungsstils, ohne dies tun zu müssen all das verrückte Synchronisationsmaterial, das bereits in den Bauch des aufgerufenen Codes gerollt ist.
Aus diesem Grund bevorzugen manche Menschen den Begriff intern synchronisiert .
Terminologiesätze
Es gibt drei Hauptbegriffe für diese Ideen, auf die ich gestoßen bin. Das erste und historisch populärere (aber schlechtere) ist:
Das zweite (und bessere) ist:
Ein dritter ist:
Analogien
thread sicher ~ threadsicher ~ intern synchronisiert
Ein Beispiel für ein intern synchronisiertes (auch als threadsicheres oder threadsicheres System bezeichnet) System ist ein Restaurant, in dem ein Host Sie an der Tür begrüßt und Sie nicht in die Warteschlange stellt. Der Gastgeber ist Teil des Mechanismus des Restaurants für den Umgang mit mehreren Kunden und kann einige ziemlich knifflige Tricks anwenden, um die Sitzplätze wartender Kunden zu optimieren, z. B. die Größe ihrer Gruppe zu berücksichtigen oder wie viel Zeit sie haben oder sogar telefonisch reservieren. Das Restaurant ist intern synchronisiert, da all dies Teil der Schnittstelle für die Interaktion mit ihm ist.
nicht threadsicher (aber nett) ~ threadkompatibel ~ extern synchronisiert ~ free-threaded
Angenommen, Sie gehen zur Bank. Es gibt eine Linie, dh Streit um die Bankangestellten. Weil Sie kein Wilder sind, erkennen Sie, dass das Beste, was Sie inmitten des Kampfes um eine Ressource tun können, darin besteht, sich wie ein zivilisiertes Wesen anzustellen. Niemand bringt Sie dazu technisch dazu. Wir hoffen, dass Sie die notwendige soziale Programmierung haben, um dies selbst zu tun. In diesem Sinne ist die Banklobby extern synchronisiert. Sollen wir sagen, dass es threadsicher ist? Das ist die Implikation, wenn Sie sich für die thread-sichere , thread-unsichere bipolare Terminologie entscheiden. Es ist kein sehr guter Satz von Begriffen. Die bessere Terminologie ist extern synchronisiert,Die Banklobby ist nicht feindlich eingestellt, auf den mehrere Kunden zugreifen können, aber sie synchronisiert sie auch nicht. Die Kunden machen das selbst.
Dies wird auch als "frei mit Gewinde" bezeichnet, wobei "frei" wie in "frei von Läusen" ist - oder in diesem Fall Sperren. Genauer gesagt, Synchronisationsprimitive. Das bedeutet nicht, dass der Code ohne diese Grundelemente auf mehreren Threads ausgeführt werden kann. Es bedeutet nur, dass sie nicht bereits installiert sind und es an Ihnen, dem Benutzer des Codes, liegt, sie selbst zu installieren, wie Sie es für richtig halten. Das Installieren eigener Synchronisationsprimitive kann schwierig sein und erfordert gründliches Nachdenken über den Code, kann aber auch zu einem schnellstmöglichen Programm führen, indem Sie die Ausführung des Programms auf den heutigen Hyperthread-CPUs anpassen können.
nicht threadsicher (und schlecht) ~ threadfeindlich ~ nicht synchronisierbar
Ein Beispiel tägliche Analogie eines faden feindlichen System ist einige Ruck mit einem Sportwagen weigern ihre Scheuklappen zu verwenden und Spurwechsel nolens volens. Ihre Fahrweise ist Thread feindlich oder unsychronizable weil Sie keine Möglichkeit , mit ihnen zu koordinieren, und dies zu Anstoß für die gleiche Spur führen kann, ohne die Auflösung und damit ein Unfall als zwei Autos versuchen , den gleichen Raum zu besetzen, ohne Protokoll verhindern Sie dies. Dieses Muster kann auch allgemein als unsozial angesehen werden, was ich bevorzuge, weil es weniger spezifisch für Threads ist und daher allgemeiner auf viele Bereiche der Programmierung anwendbar ist.
Warum thread safe et al. sind eine schlechte Terminologie gesetzt
Der erste und älteste Terminologiesatz unterscheidet nicht genauer zwischen Thread-Feindseligkeit und Thread-Kompatibilität . Die Thread-Kompatibilität ist passiver als die sogenannte Thread-Sicherheit. Dies bedeutet jedoch nicht, dass der aufgerufene Code für die gleichzeitige Verwendung von Threads nicht sicher ist. Es bedeutet nur, dass die Synchronisierung, die dies ermöglichen würde, passiv ist und den aufrufenden Code verschiebt, anstatt ihn als Teil seiner internen Implementierung bereitzustellen. Thread-kompatibel ist, wie Code wahrscheinlich in den meisten Fällen standardmäßig geschrieben werden sollte, aber dies wird leider auch oft fälschlicherweise als Thread-unsicher angesehen, als ob es von Natur aus gegen die Sicherheit gerichtet wäre, was für Programmierer ein Hauptverwirrungspunkt ist.
HINWEIS: In vielen Softwarehandbüchern wird der Begriff "threadsicher" verwendet, um sich auf "threadkompatibel" zu beziehen, was das, was bereits durcheinander war, noch verwirrender macht! Ich vermeide die Begriffe "thread-sicher" und "thread-unsicher" um jeden Preis aus diesem Grund, da einige Quellen etwas "thread-sicher" nennen, während andere es "thread-unsicher" nennen, weil sie nicht zustimmen können ob Sie einige zusätzliche Sicherheitsstandards (Synchronisationsprimitive) erfüllen müssen oder einfach NICHT feindlich eingestellt sind, um als "sicher" zu gelten. Vermeiden Sie diese Begriffe und verwenden Sie stattdessen die intelligenteren Begriffe, um gefährliche Missverständnisse mit anderen Ingenieuren zu vermeiden.
Erinnerung an unsere Ziele
Unser Ziel ist es im Wesentlichen, das Chaos zu untergraben.
Wir tun dies, indem wir deterministische Systeme schaffen, auf die wir uns verlassen können. Determinismus ist teuer, hauptsächlich aufgrund der Opportunitätskosten für den Verlust von Parallelität, Pipelining und Neuordnung. Wir versuchen, das Ausmaß an Determinismus zu minimieren, das wir benötigen, um unsere Kosten niedrig zu halten, und vermeiden gleichzeitig Entscheidungen, die den geringen Determinismus, den wir uns leisten können, weiter untergraben.
Bei der Synchronisation von Threads geht es darum, die Reihenfolge zu erhöhen und das Chaos zu verringern. Die Ebenen, auf denen Sie dies tun, entsprechen den oben genannten Begriffen. Die höchste Ebene bedeutet, dass sich ein System jedes Mal vollständig vorhersehbar verhält. Die zweite Ebene bedeutet, dass sich das System so gut verhält, dass der aufrufende Code Unvorhersehbarkeit zuverlässig erkennen kann. Zum Beispiel ein falsches Aufwecken in einer Bedingungsvariablen oder ein Fehler beim Sperren eines Mutex, weil dieser nicht bereit ist. Die dritte Stufe bedeutet, dass sich das System nicht gut genug verhält, um mit anderen zu spielen, und dass es NIEMALS Single-Threaded ausgeführt werden kann, ohne Chaos zu verursachen.
quelle
Anstatt Code oder Klassen als threadsicher zu betrachten oder nicht, halte ich es für hilfreicher, Aktionen als threadsicher zu betrachten. Zwei Aktionen sind threadsicher, wenn sie sich beim Ausführen aus beliebigen Threading-Kontexten wie angegeben verhalten. In vielen Fällen unterstützen Klassen einige Kombinationen von Aktionen threadsicher und andere nicht.
Beispielsweise garantieren viele Sammlungen wie Array-Listen und Hash-Sets, dass sie, wenn sie anfänglich ausschließlich mit einem Thread aufgerufen werden und niemals geändert werden, nachdem ein Verweis für andere Threads sichtbar wird, durch eine beliebige Kombination auf beliebige Weise gelesen werden können von Fäden ohne Störung.
Interessanter ist, dass einige Hash-Set-Sammlungen, wie die ursprüngliche nicht generische in .NET, möglicherweise eine Garantie dafür bieten, dass, solange kein Element jemals entfernt wird, und vorausgesetzt, dass nur ein Thread jemals in sie schreibt, jeder Thread, der dies versucht Das Lesen der Sammlung verhält sich so, als würde auf eine Sammlung zugegriffen, bei der Aktualisierungen verzögert und in beliebiger Reihenfolge erfolgen können, die sich jedoch ansonsten normal verhalten. Wenn Thread Nr. 1 X und dann Y hinzufügt und Thread Nr. 2 Y und dann X sucht und sieht, kann Thread Nr. 2 erkennen, dass Y existiert, X jedoch nicht. Ob ein solches Verhalten "threadsicher" ist oder nicht, hängt davon ab, ob Thread 2 bereit ist, mit dieser Möglichkeit umzugehen.
Abschließend sei angemerkt, dass einige Klassen - insbesondere das Blockieren von Kommunikationsbibliotheken - möglicherweise eine "close" - oder "Dispose" -Methode haben, die in Bezug auf alle anderen Methoden threadsicher ist, in Bezug auf jedoch keine anderen Methoden, die threadsicher sind gegenseitig. Wenn ein Thread eine blockierende Leseanforderung ausführt und ein Benutzer des Programms auf "Abbrechen" klickt, kann der Thread, der versucht, den Lesevorgang auszuführen, keine Abschlussanforderung ausgeben. Die Schließ- / Entsorgungsanforderung kann jedoch asynchron ein Flag setzen, wodurch die Leseanforderung so schnell wie möglich abgebrochen wird. Sobald ein Thread geschlossen wird, wird das Objekt unbrauchbar und alle Versuche zukünftiger Aktionen schlagen sofort fehl.
quelle
Mit 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.
quelle