Was bedeutet der Begriff „threadsicher“?

367

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?

Varun Mahajan
quelle
8
Ich
Sebastian

Antworten:

256

Aus Wikipedia:

Thread-Sicherheit ist ein Computerprogrammierungskonzept, das im Kontext von Multithread-Programmen anwendbar ist. Ein Code ist threadsicher, wenn er bei gleichzeitiger Ausführung durch mehrere Threads ordnungsgemäß funktioniert. Insbesondere muss es die Notwendigkeit erfüllen, dass mehrere Threads auf dieselben gemeinsam genutzten Daten zugreifen, und dass nur ein Thread gleichzeitig auf ein gemeinsam genutztes Datenelement zugreifen muss.

Es gibt einige Möglichkeiten, um die Gewindesicherheit zu erreichen:

Wiedereintritt:

Schreiben von Code so, dass er teilweise von einer Aufgabe ausgeführt, von einer anderen Aufgabe erneut eingegeben und dann von der ursprünglichen Aufgabe 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 jeweils nur ein Thread die gemeinsam genutzten Daten liest oder schreibt. Wenn ein Code auf mehrere gemeinsam genutzte Daten zugreift, ist große Vorsicht geboten. Zu den Problemen gehören Rennbedingungen, Deadlocks, Livelocks, Hunger und verschiedene andere in vielen Lehrbüchern für Betriebssysteme aufgeführte Krankheiten.

Thread-lokaler Speicher:

Variablen werden so lokalisiert, dass jeder Thread eine eigene private Kopie hat. Diese Variablen behalten ihre Werte über Unterroutinen 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.

Atomoperationen:

Auf gemeinsam genutzte Daten wird mithilfe von 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. Atomoperationen bilden die Grundlage vieler Thread-Verriegelungsmechanismen.

Weiterlesen:

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


Blauohr
quelle
4
Bei diesem Link fehlen technisch gesehen einige kritische Punkte. Der betreffende gemeinsam genutzte Speicher muss veränderbar sein (Nur-Lese-Speicher kann nicht threadsicher sein), und die mehreren Threads müssen a) mehrere Schreibvorgänge für den Speicher ausführen, während dessen sich der Speicher in einem inkonsistenten (falschen) Zustand befindet. state und b) anderen Threads erlauben, diesen Thread zu unterbrechen, während der Speicher inkonsistent ist.
Charles Bretana
20
Wenn nach Google gesucht wird, ist das erste Ergebnis Wiki. Es macht keinen Sinn, es hier überflüssig zu machen.
Ranvir
Was meinst du mit "einem Code, der auf eine Funktion zugreift"? Die Funktion, die selbst ausgeführt wird, ist der Code, nicht wahr?
Koray Tugay
82

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

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

Marek Blotny
quelle
34
Im gleichen Prozess!
Ali Afshar
In der Tat im gleichen Prozess :)
Marek Blotny
4
"Code zu schreiben, der wochenlang stabil läuft, erfordert extreme Paranoia." Das ist ein Zitat, das ich mag :)
Jim T
5
duh! Diese Antwort wiederholt nur die Frage! --- Und warum nur im selben Prozess ??? Wenn der Code fehlschlägt, wenn mehrere Threads ihn von verschiedenen Prozessen ausführen, ist er möglicherweise (der "gemeinsam genutzte Speicher" befindet sich möglicherweise in einer Festplattendatei) NICHT threadsicher !!
Charles Bretana
1
@ mg30rg. Vielleicht ist die Verwirrung das Ergebnis des Denkens, dass, wenn ein Codeblock von mehreren Prozessen ausgeführt wird, aber nur von einem Thread pro Prozess, dies irgendwie immer noch ein "Single-Threaded" -Szenario ist, kein Multi-Threaded-Szenario . Diese Idee ist nicht einmal falsch. Es ist nur eine falsche Definition. Es ist klar, dass mehrere Prozesse im Allgemeinen nicht synchron auf demselben Thread ausgeführt werden (außer in seltenen Szenarien, in denen Prozesse vom Design her aufeinander abgestimmt sind und das Betriebssystem Threads zwischen Prozessen teilt)
Charles Bretana,
50

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).

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 Heapspeicher, die über globale / statische Variablen erreichbar sind. 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, dem dieser Stapel gehört.
  2. Die zweite Bedingung ist, dass es eine Eigenschaft gibt (oft als Invariante bezeichnet ), 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, dass " totalRequests genau angeben muss, wie oft ein Thread einen Teil der Inkrementanweisung ausgeführt hat ". In der Regel muss diese invariante Eigenschaft true sein (in diesem Fall muss totalRequests eine genaue Anzahl enthalten), bevor eine Aktualisierung erfolgt, 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 besonderen Fall aus der Zeit totalRequests abgerufen , bis die Zeit der aktualisierte Wert gespeichert wird, wird totalRequests nicht die invariante erfüllen.
  4. Die vierte und letzte Bedingung, die auftreten muss, damit ein Rennen stattfinden kann (und damit der Code daher NICHT "threadsicher" ist), ist, dass ein anderer Thread auf den gemeinsam genutzten Speicher zugreifen kann, während die Invariante unterbrochen ist, was zu inkonsistenten oder falsches Verhalten.
Charles Bretana
quelle
6
Dies gilt nur für sogenannte Datenrennen und ist natürlich wichtig. Es gibt jedoch auch andere Möglichkeiten, wie Code nicht threadsicher sein kann - beispielsweise eine schlechte Sperrung, die zu Deadlocks führen kann. Selbst etwas Einfaches wie das Aufrufen von System.exit () irgendwo in einem Java-Thread macht diesen Code nicht threadsicher.
Ingo
2
Ich denke, bis zu einem gewissen Grad ist dies Semantik, aber ich würde argumentieren, dass ein schlechter Sperrcode, der einen Deadlock verursachen kann, den Code nicht unsicher macht. Erstens muss der Code erst gesperrt werden, wenn eine oben beschriebene Rennbedingung möglich ist. Wenn Sie dann den Sperrcode so schreiben, dass ein Deadlock verursacht wird, ist dies nicht threadsicher, sondern nur ein schlechter Code.
Charles Bretana
1
Beachten Sie jedoch, dass der Deadlock beim Ausführen von Single-Threaded nicht auftritt. Für die meisten von uns würde dies sicherlich unter die intuitive Bedeutung von (nicht) "thread-sicher" fallen.
Jon Coombs
Nun, Deadlocks können nur auftreten, wenn Sie Multithreading ausführen. Dies bedeutet jedoch, dass Netzwerkprobleme nicht auftreten können, wenn Sie auf einem Computer ausgeführt werden. Andere Probleme können auch mit einem Thread auftreten, wenn der Programmierer den Code so schreibt, dass er vor Abschluss der Aktualisierung aus den kritischen Codezeilen ausbricht und die Variable in einer anderen Unterroutine ändert.
Charles Bretana
34

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. ""

Buu Nguyen
quelle
Diese Definition ist unvollständig und nicht spezifisch und definitiv nicht umfassend. Wie oft muss es sicher laufen, nur einmal? zehn Mal? jedes Mal? 80% der Zeit? und es gibt nicht an, was es "unsicher" macht. Wenn es nicht sicher läuft, aber der Fehler darin bestand, dass ein Fehler durch Teilen durch Null vorliegt, ist es dann ein Thread - "Unsicher"?
Charles Bretana
Sei das nächste Mal höflicher und vielleicht können wir darüber diskutieren. Dies ist nicht Reddit und ich bin nicht in der Stimmung, mit unhöflichen Menschen zu sprechen.
Buu Nguyen
Ihre Interpretation von Kommentaren über die Definition eines anderen als Beleidigung für sich selbst ist aussagekräftig. Sie müssen Substanz lesen und verstehen, bevor Sie emotional reagieren können. Nichts Unhöfliches an meinem Kommentar. Ich habe auf die Bedeutung der Definition hingewiesen. Es tut mir leid, wenn Ihnen die Beispiele, die ich zur Veranschaulichung des Punktes verwendet habe, unangenehm waren.
Charles Bretana
28

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, StringBufferund StringBuilder. Der Unterschied besteht darin, dass StringBufferThread-sicher ist, sodass eine einzelne Instanz von a StringBuffervon mehreren Threads gleichzeitig verwendet werden kann. StringBuilderist 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.

Marcus Downing
quelle
22

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.

Mnementh
quelle
21

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.

Hapkido
quelle
9

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.

Bill die Eidechse
quelle
9

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 :

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 andere Koordination seitens der Code aufrufen.

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).
  • Alle Werte von 0 bis zum aktuellen Wert wurden zu einem bestimmten Zeitpunkt zurückgegeben (kein Wert wird übersprungen).

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

Assylien
quelle
8

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

Greg Balajewicz
quelle
5

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.

Tvanfosson
quelle
5

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 synchronizedund volatile-Konstrukte sowie die Thread.start()und Thread.join()-Methoden können Vor-Vor- Beziehungen bilden.

Die Methoden aller Klassen in java.util.concurrentund ihrer Unterpakete erweitern diese Garantien auf die Synchronisation auf höherer Ebene. Bestimmtes:

  1. Aktionen in einem Thread vor dem Platzieren eines Objekts in einer gleichzeitigen Sammlung erfolgen vor Aktionen nach dem Zugriff oder dem Entfernen dieses Elements aus der Sammlung in einem anderen Thread.
  2. Aktionen in einem Thread vor der Übermittlung von a Runnablean ein ExecutorEreignis, bevor dessen Ausführung beginnt. Ähnliches gilt für Callables, die an eine ExecutorService.
  3. Aktionen, die von der asynchronen Berechnung ausgeführt werden, die durch Vorabaktionen dargestellt wird Future, nachdem das Ergebnis über einen Future.get()anderen Thread abgerufen wurde .
  4. Aktionen vor dem "Freigeben" von Synchronisierungsmethoden , z. B. Lock.unlock, Semaphore.release, and CountDownLatch.countDownVorabaktionen nach einer erfolgreichen "Erfassungs" -Methode, z. B. Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.awaitfür dasselbe Synchronisierungsobjekt in einem anderen Thread.
  5. Für jedes Thread-Paar, das erfolgreich Objekte über eine ExchangerAktion vor dem austauschtexchange() in jedem Thread statt - vor denen nach dem entsprechenden Austausch () in einem anderen Thread.
  6. Aktionen vor dem Aufruf CyclicBarrier.awaitund Phaser.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.
Ravindra Babu
quelle
4

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:

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

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:

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

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.

Steve Knight
quelle
2

Beantworten wir dies anhand eines Beispiels:

class NonThreadSafe {

    private int counter = 0;

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

Die countTo10Methode 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.

rghome
quelle
1

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:

  1. fadensicher
  2. nicht threadsicher

Das zweite (und bessere) ist:

  1. fadensicher
  2. Thread-kompatibel
  3. Thread feindlich

Ein dritter ist:

  1. intern synchronisiert
  2. extern synchronisiert
  3. nicht synchronisierbar

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.

Daniel Russell
quelle
1

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.

Superkatze
quelle
0

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.

schäbig
quelle