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
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
- 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.
- Notieren Sie die Ergebnisse im atomaren Block, so dass das noch gültige Ergebnis nicht mehr als eine billige Suche nach einer Wiederholung ist.
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.
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
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. –
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.
Danke für die Referenz. – Alex
STMs sind nicht unbedingt nicht blockierend. –
Ausgezeichnetes Beispiel. Ich wollte mit so etwas testen, aber mir war die 'unsafeIOToSTM'-Funktion nicht bekannt! – Alex