2010-06-13 22 views
13

Dies ist nur ein hypothetisches Szenario, um meine Frage zu veranschaulichen. Angenommen, es gibt zwei Threads und einen TVar, der zwischen ihnen geteilt wird. In einem Thread gibt es einen atomaren Block, der den TVar liest und 10 Sekunden benötigt, um abzuschließen. In einem anderen Thread ist ein atomarer Block, der die TVar jede Sekunde verändert. Wird der erste atomare Block jemals abgeschlossen sein? Sicherlich wird es einfach weiter zum Anfang gehen, weil das Protokoll ständig in einem inkonsistenten Zustand ist?STM Monad Problem

Antwort

12

Wie andere gesagt haben: Theoretisch gibt es keine Garantie für Fortschritte. In der Praxis keine Garantie für Fortschritte gibt es auch:

import Control.Monad -- not needed, but cleans some things up 
import Control.Monad.STM 
import Control.Concurrent.STM 
import Control.Concurrent 
import GHC.Conc 
import System.IO 

main = do 
    tv <- newTVarIO 0 
    forkIO (f tv) 
    g tv 

f :: TVar Int -> IO() 
f tv = forever $ do 
    atomically $ do 
      n <- readTVar tv 
      writeTVar tv (n + 1) 
      unsafeIOToSTM (threadDelay 100000) 
    putStr "." 
    hFlush stdout 

g :: TVar Int -> IO() 
g tv = forever $ do 
    atomically $ do 
      n <- readTVar tv 
      writeTVar tv (n + 1) 
      unsafeIOToSTM (threadDelay 1000000) 
    putStrLn "Done with long STM" 

Die oben nie sagt: „Fertig mit langen STM“ in meinen Tests.

Natürlich, wenn Sie die Berechnung immer noch der Meinung sein wird, gültig/einschlägig dann würden Sie entweder

  1. den atomaren Block verlassen wollen, teure Berechnung durchführen, geben Sie den Atom-Block/bestätigt Annahmen gültig sind/und Aktualisiere den Wert. Möglicherweise gefährlich, aber nicht mehr als die meisten Schließstrategien.
  2. Notieren Sie die Ergebnisse im atomaren Block, so dass das noch gültige Ergebnis nicht mehr als eine billige Suche nach einer Wiederholung ist.
+2

Ausgezeichnetes Beispiel. Ich wollte mit so etwas testen, aber mir war die 'unsafeIOToSTM'-Funktion nicht bekannt! – Alex

2

Nein, es würde gut funktionieren. Wie genau die beiden Threads interagieren würden, hängt von der Wiederholungslogik ab.

Zum Beispiel, sagen wir, Sie haben:

ten tv = do 
    n <- readTVar tv 
    when (n < 7) retry 
    writeTVar tv 0 
    -- do something that takes about 10 seconds 

one tv = do 
    modifyTVar tv (+1) 
    -- do something that takes about 1 second 

So ist das „ten“ Thread im Wiederholungszustand sein wird, bis der TVar erreicht der Wert 7, dann wird es gehen.

Beachten Sie, dass Sie nicht direkt steuern können, wie lange diese Berechnungen innerhalb der STM Monade dauert. Das wäre ein Nebeneffekt, und Nebenwirkungen sind in STM-Berechnungen nicht erlaubt. Die einzige Möglichkeit, mit der äußeren Welt zu kommunizieren, ist über Werte, die durch den Transaktionsspeicher übertragen werden.

Und das bedeutet, dass, wenn die "Taktstock-Weitergabe" Logik durch Transaktionsspeicher ist richtig, das Programm wird korrekt unabhängig von der genauen Menge der Zeit einen Teil davon dauert. Das ist Teil der Garantie von STM.

+0

Ich denke wirklich an einen Worst-Case-Szenario. Lassen Sie uns die Retrys für einen Moment vergessen und denken Sie an zwei Threads, und betrachten Sie die Ausführung des STM 'ten'. Er liest den TVar und schreibt diesen Wert in das Protokoll. Inzwischen ändert der andere Thread diesen TVar immer während der Ausführung von 'zehn'.Am Ende der Ausführung von 'ten' ist der Wert in der echten TVar nicht der gleiche wie der anfänglich in 'ten' gelesene Wert, wodurch 'zehn' gezwungen werden, das Protokoll erneut zu initialisieren und jedes Mal erneut auszuführen. – Alex

+1

Wie Yitz sagte, es kommt darauf an. Ja, es ist möglich, eine Situation zu konstruieren, in der eine Transaktion niemals abgeschlossen werden kann. Oder formeller gesagt, STM garantiert keinen Fortschritt. –

5

STM verhindert Deadlock, ist aber immer noch anfällig für Hunger. Es ist in einem pathologischen Fall möglich, dass die 1s-Atomaktion die Ressource immer aufnimmt.

Allerdings sind die Veränderungen dieses Geschehens sehr selten - ich glaube nicht, dass ich es jemals in der Praxis gesehen habe.

Für die Semantik siehe Composable Memory Transactions, Abschnitt 6.5 "Fortschritt". STM in Haskell garantiert nur, dass eine laufende Transaktion erfolgreich festgeschrieben wird (d. H. Kein Deadlock), aber im schlimmsten Fall blockiert eine unendliche Transaktion andere.

+0

Danke für die Referenz. – Alex

+0

STMs sind nicht unbedingt nicht blockierend. –