2009-06-15 10 views
0

Ich frage mich, was ist der "beste" Weg, um Daten thread-sicher zu machen.Fadensicherheit ... Was ist meine "beste" Vorgehensweise?

Speziell muss ich eine verknüpfte Liste über mehrere Threads schützen - ein Thread könnte versuchen, davon zu lesen, während ein anderer Thread Daten hinzufügt/entfernt, oder sogar die gesamte Liste freigibt. Ich habe über Schlösser gelesen; Sie scheinen der am häufigsten verwendete Ansatz zu sein, aber sie können offensichtlich problematisch sein (Deadlocks). Ich habe auch über Atom-Operationen sowie Thread-lokalen Speicher gelesen.

Was wäre Ihrer Meinung nach meine beste Vorgehensweise? Was ist der Ansatz, den die meisten Programmierer verwenden, und aus welchem ​​Grund?

+2

beste Ansatz ist abhängig von der Sprache, die Sie verwenden. Zum Beispiel in Erlang ist dies ein Nicht-Problem –

+0

Yep, Erlangs _intrinsically_ Message-Weitergabe (so Multiprocessing ist die süßeste Sache in ihr) - in meiner Antwort skizzierte ich, wie man diesen glücklichen Zustand in anderen Sprachen wie Python bauen. –

Antwort

5

Ein Ansatz, der nicht stark genutzt wird, aber ganz gesund, ist ein Zweck Thread zu bezeichnen jede „shared“ Struktur zu besitzen. Dieser Thread sitzt im Allgemeinen auf einer (thread-sicheren ;-) Warteschlange, z.B. in Python eine Queue.Queue Instanz für Arbeitsanfragen (Lesen oder Ändern der gemeinsamen Struktur), einschließlich derer, die eine Antwort anfordern (sie werden ihre eigene Warteschlange übergeben, auf die die Antwort gesetzt wird, wenn sie bereit ist) und solche, die dies nicht tun. Dieser Ansatz serialisiert den gesamten Zugriff auf die gemeinsam genutzte Ressource vollständig, lässt sich leicht in eine Multi-Prozess- oder verteilte Architektur umwandeln (fast hirnlos, in Python, mit multiprocessing ;-)) und garantiert absolute Solidität und das Fehlen von Deadlocks sowie Race Conditions Das zugrunde liegende Warteschlangenobjekt ist ein für allemal gut programmiert.

Es macht im Grunde die Hölle von geteilten Datenstrukturen in das Paradies der Message-passing Concurrency-Architekturen.

OTOH, es Mai ein bisschen mehr Overhead als slugging es auf die harte Tour mit Schlössern & C ;-).

+0

stimme zu. Ich würde viel lieber zu einer Message-Passing-basierten Schnittstelle als Support gleichzeitige Hinzufügen/Entfernen und Traversal auf einer verknüpften Liste verschieben. – Rick

+0

Sie müssen jedoch immer noch auf Deadlocks in einem reinen Nachrichtenübergabesystem achten ... wenn Prozess A auf Nachricht b von Prozess B wartet, bevor er Nachricht a an Prozess C sendet -AND- Prozess B sendet Nachricht b nur dann, wenn es empfängt die Nachricht c von dem Prozess C -AND- Prozess C gibt nur die Nachricht c aus, wenn er die Nachricht a empfängt. Dasselbe grundlegende Problem wie ein Deadlock, das nur von Nachrichtenabhängigkeiten anstelle von gesperrten Ressourcen abhängig ist. –

+0

In der Windows-API ist dies der Unterschied zwischen SendMessage (das synchron ist und den Absender blockiert, bis der Sendevorgang empfangen und verarbeitet wurde) und PostMessage (der asynchron ist). Asynchrone Message-Passing ist nicht anfällig für Deadlock (aber möglicherweise haben Sie sich gefragt, ob Ihr Post jemals tatsächlich erhalten und verarbeitet wurde). – ChrisW

0

Denken Sie immer an die wichtigste Regel der Fadensicherheit. Kenne alle kritischen Abschnitte deines Codes genauestens. Und dadurch, kenne sie wie dein ABC. Nur wenn Sie sie auf Knopfdruck identifizieren können, werden Sie wissen, in welchen Bereichen Sie die Sicherheitsmechanismen für den Faden anwenden.

Danach, erinnern sich an die Faustregeln:

  • Halten Sie Ausschau nach allen globalen Variablen/Variablen auf dem Heap.
  • Stellen Sie sicher, dass Ihre Subroutinen Re-Entrant sind.
  • Stellen Sie sicher, dass der Zugriff auf freigegebene Daten serialisiert ist.
  • Stellen Sie sicher, dass es keine indirekten Zugriffe über Zeiger gibt.

(Ich bin sicher, andere können mehr hinzufügen.)

0

Der "beste" Weg, aus Sicherheitsgründen, besteht darin, die gesamte Datenstruktur zu sperren, so dass nur ein Thread sie gleichzeitig berühren kann.

Sobald Sie sich entscheiden, weniger als die gesamte Struktur zu sperren, vermutlich aus Leistungsgründen, sind die Details dazu chaotisch und unterscheiden sich für jede Datenstruktur und sogar Varianten der gleichen Struktur.

Mein Vorschlag ist zu

  1. Beginnen Sie mit einer globalen Sperre auf Ihrer Datenstruktur. Profiliere dein Programm, um zu sehen, ob es wirklich ein Problem ist.

  2. Wenn es ein Problem ist, überlegen Sie, ob es eine andere Möglichkeit gibt, das Problem zu verteilen. Können Sie die Datenmenge in der fraglichen Datenstruktur minimieren, sodass nicht so oft oder so lange auf sie zugegriffen werden muss? Wenn es sich beispielsweise um ein Warteschlangensystem handelt, können Sie möglicherweise eine lokale Warteschlange pro Thread beibehalten und nur Objekte in eine globale Warteschlange verschieben oder daraus entfernen, wenn eine lokale Warteschlange über- oder unterbeladen wird.

  3. Sehen Sie sich Datenstrukturen an, die entwickelt wurden, um Konflikte für die bestimmte Art von Dingen, die Sie tun, zu reduzieren, und setzen Sie sie sorgfältig und präzise um, wobei Sie auf der sicheren Seite bleiben. Für das Warteschlangen-Beispiel könnten Work-Stealing-Warteschlangen das sein, was Sie brauchen.

+0

Bei diesem Ansatz wird auch davon ausgegangen, dass eine "gesamte Struktur" eigenständig ist und Sie nie mehr als ein Objekt gleichzeitig sperren müssen. Normalerweise tritt das Problem mit den Struktursperren global auf, wenn Daten in einer Struktur auf Daten in einer anderen Struktur verweisen. Dann werden Deadlocks zum allgemeinen Ort. – jmucchiello

+0

Wenn Sie mehrere Strukturen sperren müssen und mehrere Sperren anstelle einer Sperre verwenden, um alle gleichzeitig zu sperren, befinden Sie sich in der gleichen Position, als würden Sie mehrere Sperren verwenden, um verschiedene Teile einer Datenstruktur zu sperren. –

2

Sie könnten eine unveränderbare Sammlung betrachten. Ähnlich wie eine Zeichenfolge in .net über Methoden wie Ersetzen, Einfügen usw. verfügt. Sie ändert die Zeichenfolge nicht, sondern erstellt stattdessen eine neue. Eine LinkedList-Auflistung kann ebenfalls als unveränderlich definiert werden. Tatsächlich ist eine LinkedList im Vergleich zu einigen anderen Auflistungsdatenstrukturen tatsächlich ziemlich einfach zu implementieren.

Hier ist ein Link zu einem Blogbeitrag, in dem unveränderbare Sammlungen und ein Link zu einigen Implementierungen in .NET besprochen werden.

http://blogs.msdn.com/jaredpar/archive/2009/04/06/immutable-vs-mutable-collection-performance.aspx

+0

Diese Art von Datenstrukturen werden oft als "persistente" Datenstrukturen bezeichnet (und für solche Diskussionen gibt es ein "persistent-data-structures" -Tag auf SO). Diese können sich häufig Speicherzellen mit früheren Versionen der Datenstrukturen teilen, wodurch das Kopieren (und die Speichernutzung, wenn Sie die früheren Versionen der Datenstrukturen behalten) reduziert wird. –

Verwandte Themen