2015-04-22 8 views
13

Wie kann ich ein Thread-sicheres globales Array mit minimalen Änderungen definieren?Wie definiere ich ein Thread-sicheres Array?

Ich möchte wie jeder Zugriff darauf mit Mutex und synchronisierten Block erreicht werden.

So etwas wie ‚T‘ wird eine Art sein (beachten Sie, dass ‚sync‘ Schlüsselwort ist nicht AFAIK definiert ist):

sync Array!(T) syncvar; 

Und jeder Zugriff darauf wird diese simmilar sein:

Mutex __syncvar_mutex; 

    //some func scope.... 
    synchronized(__syncvar_mutex) { /* edits 'syncvar' safely */ } 
+0

das Array Schutz selbst einfach ist. Sie müssen nur eine Wrapper-Struktur erstellen, in der alle Array-Funktionen und Operatoren überladen sind und alle Funktionen synchronisiert sind. Das Problem besteht darin, an die Elemente des Arrays selbst heranzukommen. Auch diese zu schützen wird sehr viel komplizierter, da sie, sobald sie von einer Funktion wie 'opIndex' zurückgegeben werden, nicht mehr geschützt sind ... –

+0

Ich dachte, das ist was 'shared (T [])' angenommen wurde zu tun, aber anscheinend nicht .... –

+0

@ AdamD.Ruppe Alle geteilten wirklich tut es, damit die Variable über Threads geteilt wird, anstatt thread-local zu sein. Es gibt ein paar Fälle, in denen der Compiler sich beschweren wird, wenn Sie versuchen, mit Shared zu arbeiten, was garantiert ein Problem darstellt (in einigen Fällen mit atomaren Operationen IIRC verbunden), aber nichts mit Mutexe oder Synchronisation macht. Das liegt an dir. Synchronisierte Klassen sind der empfohlene Weg, um gemeinsam genutzte Objekte zu behandeln, aber sie sind nicht vollständig implementiert (nur synchronisierte Funktionen) und alles, was dieser Klasse entgeht, wird nicht mehr geschützt. –

Antwort

-1

Sie haben die richtige Idee. Als Array müssen Sie Informationen bearbeiten und abrufen können. Ich schlage vor, dass Sie sich die von Phobos zur Verfügung gestellten Dienstprogramme read-write mutex und atomic ansehen. Ein Lesevorgang ist recht einfach:

  1. synchronize auf mutex.readLock
  2. Last (mit atomicLoad)
  3. das Element kopieren
  4. aus dem synchronize Block
  5. Rückkehr der kopierte Element

Schreiben sollte sei fast genau gleich. Just syncronize auf mutex.writeLock und tun Sie einen cas oder atomicOp Operation.

Beachten Sie, dass dies nur funktioniert, wenn Sie die Elemente im Array während eines Lesevorgangs kopieren. Wenn Sie eine Referenz erhalten möchten, müssen Sie jedes Mal, wenn Sie auf das Element zugreifen oder es ändern, eine zusätzliche Synchronisierung für das Element durchführen.

+0

Ich vertraute dem System und ich fühle, dass es mich wieder versagen wird. Sag mir, glaubst du, du verdienst '+150' Ruf mit dieser Antwort? – AnArrayOfFunctions

+0

Funktioniert es? Das ist wirklich alles was zählt, nicht wahr? Wenn du denkst, dass meine Antwort es wert ist, dann ja. Du hast die Frage gestellt. Ich habe dir eine Antwort gegeben. Wenn Sie es für unzureichend halten, können wir sehen, wie es geändert werden kann. – Straivers

2

Mein naiver Versuch war, so etwas zu tun:

import std.typecons : Proxy: 

synchronized class Array(T) 
{ 
    static import std.array; 
    private std.array.Array!T data; 
    mixin Proxy!data; 
} 

Leider funktioniert es nicht wegen https://issues.dlang.org/show_bug.cgi?id=14509

Kann ich nicht sagen, dass mir sehr überrascht, obwohl als automagischen Umgang mit multi- Threading über versteckte Mutexe ist in modernen D sehr unidiomatisch und das Konzept synchronisierter Klassen ist meist ein Relikt aus D1-Zeiten.

Sie können die gleiche Lösung natürlich manuell implementieren, indem Sie eine eigene SharedArray Klasse mit allen notwendigen Methoden definieren und Sperren innerhalb der Methoden hinzufügen, bevor Sie interne private einfache Methoden aufrufen Array. Aber ich nehme an, Sie wollen etwas, das mehr aus der Box funktioniert.

Kann nichts besseres hier und jetzt erfinden (wird mehr darüber nachdenken), aber es ist erwähnenswert, dass es in D im Allgemeinen ermutigt wird, Datenstrukturen für den expliziten Umgang mit gemeinsamem Zugriff zu erstellen, statt nur normale Daten zu schützen Strukturen mit Mutexen. Und natürlich ist der am meisten unterstützte Ansatz, keine Daten gemeinsam zu nutzen, sondern stattdessen die Nachrichtenübermittlung zu verwenden.

Ich werde die Antwort aktualisieren, wenn etwas besser in meinen Sinn kommt.

+0

Nein - ich will etwas, das funktioniert und nicht zu lang ist. Danke trotzdem - ich werde es versuchen. – AnArrayOfFunctions

0

Sie können das Array innerhalb einer Struktur umschließen, die den Zugriff auf das Array sperrt, wenn ein Thread ein Token erwirbt und bis es es freigibt.

Die Hülle/Locker:

  • acquire(): wird in Schleife durch einen Thread bezeichnet. Wenn ein Zeiger zurückgegeben wird, weiß der Thread, dass er das Token hat, wenn die Methode einen Nicht-Null-Wert zurückgibt.
  • release(): wird von einem Thread nach der Verarbeitung der Daten aufgerufen, deren Zugriff zuvor erworben wurde.

.

shared struct Locker(T) 
{ 
    private: 
     T t; 
     size_t token; 
    public: 
     shared(T) * acquire() 
     { 
      if (token) return null; 
      else 
      { 
       import core.atomic; 
       atomicOp!"+="(token, 1); 
       return &t; 
      } 
     } 
     void release() 
     { 
      import core.atomic; 
      atomicOp!"-="(token, 1); 
     } 
} 

und ein Schnelltest:

alias LockedIntArray = Locker!(size_t[]); 
shared LockedIntArray intArr; 

void arrayTask(size_t cnt) 
{ 
    import core.thread, std.random; 

    // ensure the desynchronization of this job. 
    Thread.sleep(dur!"msecs"(uniform(4, 20))); 

    shared(size_t[])* arr = null; 
    // wait for the token 
    while(arr == null) {arr = intArr.acquire;} 

    *arr ~= cnt;  
    import std.stdio; 
    writeln(*arr); 

    // release the token for the waiting threads 
    intArr.release; 
} 

void main(string[] args) 
{ 
    import std.parallelism; 
    foreach(immutable i; 0..16) 
    { 
     auto job = task(&arrayTask, i); 
     job.executeInNewThread(); 
    } 
} 

Mit dem Nachteil, dass jeder Block der Betrieb über die Anordnung muss mit einem acquire/Release Paar umgeben sein.

+0

Nun - ich könnte das gleiche erreichen mit 'synchronisierten' Block. Kein wirklicher Vorteil hier? – AnArrayOfFunctions

+0

Jeder hat Ihnen schon gesagt, wie es geht, es gibt keinen einfachen Weg (minimale Änderungen): Umwickeln Sie ein Array in einer Struktur und überlasten Sie die Operatoren, die Sie interessieren.Михаил Страшун Lösung wäre in Ordnung, aber wie er bemerkt hat, funktioniert das nicht, was ein sehr interessantes Konzept für eine _solution_ BTW ist. Ist dir klar, dass du es schon seit Tagen schreiben könntest (ich meine den Wrapper)? Vergessen Sie auch nicht, dass Sie eine bessere Antwort auf das NG/Forum bekommen könnten. Nicht jeder mag SO. –

0

Es ist ziemlich einfach, einen Wrapper um ein Array zu erstellen, der es threadsicher macht. Es ist jedoch äußerst schwierig, ein thread-sicheres Array zu erstellen, das keinen Nebenläufigkeitsengpass darstellt.

Die nächste Sache, die den Sinn kommt, ist CopyOnWriteArrayList Klasse Java, aber auch das ist nicht ideal ...

+0

Ich weiß das, aber es ist mir egal, weil es genau das ist, was ich will (Nebenläufigkeits-Engpass). Aber während ich die Antworten durchblättere - es erscheint mir nicht "ziemlich einfach". – AnArrayOfFunctions

Verwandte Themen