2016-04-10 4 views
1

Ich habe zwei Future s, von denen die zweite nach dem ersten beendet wird. Beide schreiben in die gleiche ArrayBuffer Instanz, aber da sie seriell (nicht gleichzeitig) ausgeführt werden, betrachte ich sie nicht gleichzeitig.Verwenden Sie ArrayBuffer in sequentiell ausgeführten Threads?

Allerdings weiß ich, dass es die @volatile Annotation für Variablen gibt, die von zwei oder mehr Threads geteilt werden (@volatile deaktiviert Caching).

Da nach dem ersten Thread beendet ist, im Inneren des ArrayBuffer Beispiel könnte es einige Caching los, dass sein macht es unmöglich für das zweite Gewinde der ArrayBuffer ‚s wirklichen Zustand zu sehen: Ich bin nicht sicher, ob es sicher ist, ArrayBuffer auf diese Weise zu verwenden.

Ist es wahr, dass Caching ein Problem in meiner Situation sein kann, und wenn dies der Fall ist: Gibt es eine empfohlene Möglichkeit ArrayBuffer intern @volatile zu verwenden?

Antwort

2

Es sollte in Ordnung iff (wenn-und-nur-wenn) Sie es [das Array] durch die Zukunft propagieren:

val futureA = Future { 
    val buf = ArrayBuffer(…) 
    update(buf) 
    buf 
} 

val futureB = futureA map { 
    buf => moreUpdates(buf); buf 
} 

futureB foreach println // print the result of the transformations 

Dies ist aus einem Speicher sicherheitstechnischer Sicht in Ordnung, weil der Fertigstellung futureAhappens-before die onComplete (praktisch alle Transformationen auf Future ist auf onComplete implementiert) Callback wird aufgerufen. In diesem Fall map.

+0

Leider ist dies derzeit nicht der Fall: Der 'ArrayBuffer'" besucht "die zwei Threads durch zwei Callbacks für jede Datei. Daher sieht der 'ArrayBuffer' keinen Unterschied zwischen den beiden Threads. Aber der zweite Thread (die zweite Zukunft) wird durch die OnComplete der ersten Zukunft gestartet. – ideaboxer

+0

Warum onComplete und nicht flatMap? –

+0

Da 'onComplete' ist ausreichend für meine Bedürfnisse. Inwiefern ist das für meine Frage relevant? – ideaboxer

1

Das Problem ist nicht Caching per se, aber die Tatsache, dass ein ArrayBuffer ein Verbund ist, mit mehreren Unterfeldern, die im Konzert aktualisiert werden müssen, um den korrekten Betrieb sicherzustellen. Um dies zu gewährleisten, müssen Sie Tools zur Thread-Synchronisierung verwenden.

class ArrayBufferWrapper[T](ab: ArrayBuffer[T]) { 
    def add(item: T) = { 
    this.synchronized { 
     ab.add(item) 
    } 
    } 
} 

Durch die ArrayBuffer Umwickeln werden die Komponenten richtig in den aktuellen Thread realisiert, und Sie Thread-sichere add Betrieb gewährleisten.

+0

Kann ich nicht einfach eine 'ConcurrentLinkedQueue' verwenden? – ideaboxer

1

Nein, es ist nicht sicher.

Dies ist genau der Grund, warum sie die funktionale Programmierung erfunden haben. Wenn Sie sowieso scala verwenden, können Sie auch das Paradigma nutzen, das es bietet.

Vermeiden Sie die Verwendung veränderlicher Strukturen, oder lassen Sie sie in den seltenen Fällen, in denen Sie sie verwenden müssen, nicht aus dem lokalen Geltungsbereich. Dann musst du dich nie mit solchen Problemen herumschlagen. Sie werden einfach nicht mehr existieren.

Erzählen Sie uns mehr über das, was Sie versuchen zu tun, und ich bin sicher, jemand wird ein Design oder zwei vorschlagen, nicht zwei Threads mutieren die gleiche Struktur.

+0

Die erste Zukunft sammelt Tausende von Dateien und hängt sie an den Array-Puffer an. Die zweite Zukunft findet weitere Dateien und hängt sie ebenfalls an. Ich könnte einfach eine unveränderliche Repräsentation des Array-Puffers exportieren, nachdem die erste Zukunft fertig ist, und lassen Sie es so funktionieren. Aber ich fürchte, das ist ein unnötig teures Kopieren von riesigen Datenmengen ... – ideaboxer

+0

BTW. Es ist falsch, den lokalen Bereich nicht zu verlassen, was mich davor bewahrt, mit solchen Problemen fertig zu werden: Die 'ArrayBuffer'-Instanz wird niemals von außerhalb des lokalen Bereichs aufgerufen, sondern der lokale Bereich erstreckt sich über drei verschiedene Threads. – ideaboxer

+0

Warum beginnen sie sequentiell?Es hört sich so an, als müsste so etwas funktionieren: 'Future {findFiles} zip Zukunft {findMoreFiles} map {case (a, b) => a ++ b}' – Dima