Frage

Diese Frage führte mich über thread-lokale Speicher in High-Level-Entwicklungs-Frameworks zu fragen, wie Java und .NET.

Java hat eine ThreadLocal<T> Klasse (und vielleicht andere Konstrukte), während .NET hat Datenschlitzen , und bald eine ThreadLocal<T> Klasse seine eigene. (Es hat auch den ThreadStaticAttribute , aber ich bin besonders Interesse an lokalen Thread-Speicher für Mitgliederdaten.) die meisten anderen modernen Entwicklungsumgebungen bieten einen oder mehrere Mechanismen für sie, entweder in der Sprache oder Rahmenebene.

Welche Probleme nicht Thread-Local Storage lösen, oder welche Vorteile Thread-Local Storage bietet über das objektorientierten Standard Idiom der Schaffung von separaten Objektinstanzen lokalen Thread-Daten enthalten? Mit anderen Worten, wie ist das:

// Thread local storage approach - start 200 threads using the same object
// Each thread creates a copy of any thread-local data
ThreadLocalInstance instance = new ThreadLocalInstance();
for(int i=0; i < 200; i++) {
    ThreadStart threadStart = new ThreadStart(instance.DoSomething);
    new Thread(threadStart).Start();
}

Superior das?

// Normal oo approach, create 200 objects, start a new thread on each
for(int i=0; i < 200; i++) {
    StandardInstance standardInstance = new StandardInstance();
    ThreadStart threadStart = new ThreadStart(standardInstance.DoSomething);      
    new Thread(threadStart).Start();
}

kann ich sehen, dass ein einzelnes Objekt mit lokaler Thread-Speicher mit etwas speichereffizienter sein könnte und benötigt weniger Prozessorressourcen durch weniger Zuweisungen (und Konstruktionen). Gibt es noch andere Vorteile?

War es hilfreich?

Lösung

  

Welche Probleme nicht Thread-Local Storage lösen, oder welche Vorteile bietet Thread-Local Storage bietet über das objektorientierten Standard Idiom der Schaffung von separaten Objektinstanzen lokalen Thread-Daten enthalten?

Thread lokale Speicher ermöglichen es Ihnen, jeden laufenden Thread mit einer einzigartigen Instanz einer Klasse zu schaffen, die sehr wertvoll ist, wenn sie mit nicht-Thread-Klassen zu arbeiten versuchen, oder wenn sie versuchen, Synchronisationsanforderungen zu vermeiden, die aufgrund von gemeinsam genutzten Zustand auftreten kann.

Was den Vorteil gegenüber Ihrem Beispiel - wenn Sie einen einzelnen Thread laichen, ist es wenig oder kein Vorteil bei der Verwendung lokale Threadspeicher über in einer Instanz übergeben. ThreadLocal<T> und ähnliche Konstrukte werden unglaublich wertvoll, aber wenn (direkt oder indirekt) mit einem Threadpool arbeiten.

Zum Beispiel habe ich einen bestimmten Prozess, den ich in letzter Zeit gearbeitet, wo wir einige sehr schwere Berechnung unter Verwendung der neuen Task Parallel-Bibliothek in .NET tun. Bestimmte Abschnitte der durchgeführten Berechnungen können zwischengespeichert werden, und wenn der Cache-Speicher ein bestimmtes Spiel enthält, können wir ziemlich viel Zeit abscheren, wenn ein Element der Verarbeitung. Allerdings hatten die im Cache gespeicherten Informationen einen hohen Speicherbedarf, so wollten wir nicht mehr als der letzten Verarbeitungsschritt cachen.

Allerdings versuchen, diesen Cache über Threads zu teilen, ist problematisch. Um dies zu tun, würden wir den Zugriff auf sie synchronisieren müssen, und auch einige zusätzliche Kontrollen innerhalb unserer Klasse hinzufügen, um sie Thread-sicher zu machen.

Statt dies zu tun, schrieb ich den Algorithmus jedem Thread zu ermöglichen, über einen eigenen Cache in einem ThreadLocal<T> zu halten. Auf diese Weise kann die Fäden zu jeweils eigenen, privaten Cache. Da das Partitionierungsschema verwendet die TPL neigt zusammen Blöcke von Elementen zu halten, wobei jedes lokale Cache des Threads neigte die entsprechenden Werte enthalten, es erforderlich ist.

Dies eliminiert die Synchronisierungsprobleme, sondern erlaubt uns auch, unsere Caching an Ort und Stelle zu halten. Der Gesamtnutzen war recht groß, in dieser Situation.

Für ein konkretes Beispiel, werfen Sie einen Blick auf dieser Blog-Post ich auf Aggregation der TPL verwenden. Intern wird die Parallel-Klasse verwendet eine ThreadLocal<TLocal>, wenn Sie die FürJeden Überlastung dass hält lokalen Zustand (und die Parallel.For<TLocal> Methoden, auch). Dies ist, wie der lokale Zustand pro Faden getrennt gehalten wird Verriegelung zu vermeiden.

Andere Tipps

Nur gelegentlich ist es hilfreich, lokale Thread-Zustand zu haben. Ein Beispiel ist für ein Protokoll Kontext -. Es kann nützlich sein, um den Kontext zu setzen, von denen Sie verlangen, sind zur Zeit der Wartung, oder etwas ähnliches, so dass Sie alle Protokolle sammeln kann mit diesem Antrag zu tun

Ein weiteres gutes Beispiel ist System.Random in .NET. Es ist ziemlich allgemein bekannt, dass Sie nicht eine neue Instanz jedes Mal, die Sie verwenden möchten Random schaffen sollte, so dass einige Menschen eine einzelne Instanz erstellt und in einer statischen Variablen setzen ... aber das ist umständlich, weil Random ist nicht Thread-sicher. Stattdessen möchten Sie wirklich eine Instanz pro Thread, in geeigneter Weise ausgesät. ThreadLocal<T> funktioniert gut für diese.

ähnliche Beispiele sind die Kultur mit einem Thread zugeordnet ist, oder der Sicherheitskontext.

In der Regel ist es ein Fall von nicht wollen, zu viel Kontext passieren Runde ganz über dem Platz. Sie könnte jeder einzelne Methodenaufruf ein „RandomContext“ oder eine „LogContext“ gehören machen - aber es wäre in der Art und Weise Ihrer API Sauberkeit - und die Kette würde gebrochen werden, wenn Sie jemals in musste anrufen eine andere API, die ähnliche zurück zu Ihnen über eine virtuelle Methode oder etwas nennen würde.

Aus meiner Sicht lokalen Thread-Daten sind etwas, das vermieden werden sollte, wo möglich -. Aber nur gelegentlich kann es wirklich nützlich sein

Ich würde sagen, dass in die meisten Fälle Sie wegkommen kann damit statisch zu sein - aber nur gelegentlich Sie könnte wollen pro Instanz, pro Thread Informationen. Wieder ist es die Mühe wert Urteil, um zu sehen, wo es sinnvoll ist.

Es hilft, einen Wert auf den Stapel vorbei. Es kommt praktisch, wenn Sie einen Wert auf dem Call-Stack benötigen, aber es gibt keine Möglichkeit (oder Nutzen) diesen Wert an den Ort zu übergeben Sie sie als Parameter ein Verfahren benötigt wird. Das obige Beispiel für den aktuellen Httprequest in einem ThreaLocal Speicherung ist ein gutes Beispiel dafür. Die Alternative zum Httprequest als Parameter auf den Stapel passieren würde, wo ich gebraucht werden würde

In Java Thread-lokale Speichern können in einer Web-Anwendung nützlich sein, wenn ein einziger Antrag wird in der Regel von einem bestimmten Thema bearbeitet. Nehmen Spring Security zum Beispiel der Sicherheitsfilter werden die Authentifizierung durchführt und dann speichern Sie die Benutzer-Anmeldeinformationen in einem Thread lokalen Variable.

Auf diese Weise kann der tatsächliche Anforderungsverarbeitung Code Zugriff auf die aktuellen Benutzer Anforderungs- / Authentifizierungsinformationen haben, ohne irgendetwas anderes, um den Code injizieren zu müssen.

Sie möchten eine Reihe von Anrufen machen, ubiquitär eine Variable zugreifen. Sie können es als Argument in jedem Aufruf übergeben

function startComputingA(other args) {
  global_v = create // declared locally
  call A2(other args, global_v)
  call A3(other args, global_v)

function A2(other args, global_v) {
  call A3(other args, global_v)

function A3(other args, global_v) {
  call A4(other args, global_v)

Alle Ihre Funktionen müssen global_v Argument deklarieren. Dieses saugt. Sie haben einen globalen Rahmen für jede einzelne Routine globale Variablen und routen „virtuell“ Speicher

variable global_v;
function A() { // use global_v and call B() }
function B() { // use global_v and call C() }

Und doch kann es vorkommen, dass ein anderer Thread beginnt einige dieser Funktionen inzwischen ausgeführt wird. Dies wird korrupt Ihre globale Variable. Also, wollen Sie die Variable für alle Routinen sichtbar global sein, noch nicht zwischen Threads. Sie wollen, dass jeder Thread eine separate Kopie von global_v haben. Hier ist, wenn der lokale Speicher ist unverzichtbar! Sie erklären global_v als Thread-lokale Variable. So können alle Fäden global_v von überall zugreifen, aber unterschiedliche Kopien davon.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top