2016-09-22 3 views
4

ich nicht scheinen, um herauszufinden, warum die folgenden Code Deadlocks:Deadlock mit synchronisierten und scala Futures

"Locks around blocking futures" should "be re-entrant" in { 
    val lock = new Object() 

    def processInFuture = { 
     lock.synchronized { 
     // simulate async call made blocking 
     Await.result(Future { 
      blocking { 
      logger.info("Sleeping") 
      Thread.sleep(100) 
      logger.info("Waking") 
      } 
     }, Duration.Inf) 
     } 
    } 

    // fire off 10 async events and wait on each one 
    (0 until 10). 
     map(_ => Future { processInFuture }). 
     foreach(future => Await.result(future, Duration.Inf)) 
    } 

Ich kann nicht sehen, warum ein asynchrones in sync in einem kritischen Abschnitt macht die ganze Gabel- verursacht schließe dich dem Pool an.

Wenn ich die Zukunft in einem separaten Pool aus dem Fork Join Pool ausführen, funktioniert es. Ich verstehe nicht, warum der Fork-Join-Pool-Thread die anderen Threads nicht blockiert und dann erst beendet? Liegt es daran, dass der Pool irgendwie blockiert ist?

Ich weiß, es ist immer am besten, alles zu machen async, wenn seine async, aber einige Szenarien für das nicht zulassen (zum Beispiel Cache-Population mit einer Guave-Cache)

- EDIT

Zur Veranschaulichung @ Dimas Antwort das funktioniert

"Locks around blocking futures" should "be re-entrant" in { 
    val lock = new Object() 

    def processInFuture = { 
     blocking { 
     lock.synchronized { 
      // simulate async call made blocking 
      Await.result(Future { 
      blocking { 
       logger.info("Sleeping") 
       Thread.sleep(100) 
       logger.info("Waking") 
      } 
      }, Duration.Inf) 
     } 
     } 
    } 

    // fire off 10 async events and wait on each one 
    (0 until 10). 
     map(_ => Future { processInFuture }). 
     foreach(future => Await.result(future, Duration.Inf)) 
    } 
+0

Wenn dies ein gängiges Muster ist, ist der Code, den Sie geschrieben haben, falsch. Mit welchem ​​Protokollierungsrahmen arbeiten Sie? – Falmarri

+0

Abgesehen von der Frage, warum dieses Beispiel blockiert, sollten Sie vielleicht erklären, was Sie wirklich tun möchten, da dieses Beispiel im Allgemeinen als pathologisch betrachtet wird. Es gibt Lösungen, die existieren (z. B. akka), die viel besser geeignet sind, den gemeinsamen veränderbaren Zustand zu schützen, während mit asynchronen Konstrukten wie Futures gearbeitet wird. Ich kann mir eigentlich keinen vernünftigen Grund vorstellen, Locks mit Futures zu mischen, es sei denn, das Schloss war in Code, auf den Sie keinen Zugriff hatten. –

+0

Ich stieß auf ein Szenario, in dem ein Client, den ich nicht besitze, dies grundsätzlich tut und ich diesen synchronen Aufruf von anderen Async-Aufrufen als Teil eines trägen Caches schützen muss.Ich hätte es als sicher angenommen, einen asynchronen Anruf in einen Synchronisierungsanruf zu machen und ihn so in einem kritischen Abschnitt zu behandeln, ob das eine gute Idee ist oder nicht – devshorts

Antwort

6

Die maximale Anzahl von Threads im Standardpool entspricht einer Anzahl von Kernen, die Sie haben. Sagen Sie, 8. Hier ist, was passiert.

Der erste Thread tritt in den kritischen Bereich ein und beginnt zu schlafen (belegt einen anderen Thread). Sechs weitere Threads werden gestartet und stapeln sich am Eingang des synchronisierten Blocks. (Es gibt noch drei weitere Anrufe zu starten, aber im Moment sind keine Threads verfügbar, also warten sie).

Der schlafende Thread wacht auf, erfüllt diese Zukunft und der erste Thread verlässt den synchronisierten Block und endet. Die beiden Threads werden in den Pool zurückgegeben, sodass sofort zwei weitere Futures gestartet werden.

Eines der Threads, die auf das Schloss warten, wacht auf und tritt in den kritischen Bereich ein.

Jetzt gibt es keine verfügbaren Threads im Pool: 7 Fäden für die Sperre warten, und man ist innerhalb des kritischen Abschnitt, für die insgesamt 8

Der aktive Thread die Zukunft vorlegen will, das wird schlafen, wird aber blockiert, weil keine weiteren Threads im Pool verfügbar sind.

Nun warten 7 Threads darauf, dass die Sperre aufgehoben wird, und derjenige, der die Sperre hält, wartet darauf, dass ein anderer Thread verfügbar wird.

-> Deadlock.

aktualisiert vergessen zu erwähnen: die Art und Weise, es zu beheben ist blocking um den processInFuture Anruf zu setzen. Das wird den Pool erkennen lassen, dass es die Grenze für Threads überschreiten muss.

+0

@devshorts Siehe das Update, das ich gerade zur Antwort hinzugefügt habe. Vielleicht finden Sie es hilfreich ... – Dima

+0

das ist unglaublich hilfreich. Ich habe nicht realisiert, dass man eine Blockade um eine nicht zukünftige und eine synchrone Blockade setzen kann! – devshorts

+2

Große Erklärung, ich würde nur hinzufügen, dass es keine gute Idee ist, Sperren mit Scala Futures zu verwenden, es wird dringend empfohlen, alles auf eine asynchrone Art und Weise zu implementieren – Mikel