Eine Idee: Kombinieren Sie timeout
(vorgeschlagen von @MathematicalOrchid) mit änderbaren Variablen aus SafeSemaphore Zwischenwert zu speichern, jedes Mal, wenn Ihr Prozess ein Teilergebnis berechnet:
import Control.Monad
import Control.Concurrent.MSampleVar
import System.Timeout
fac :: Integer -> Integer
fac 0 = 1
fac n = n * fac (n - 1)
tmo :: Int -> ((a -> IO()) -> IO()) -> IO (Maybe a)
tmo ms f = do
mvar <- newSV Nothing
timeout ms (f (writeSV mvar . (Just $!)))
readSV mvar
longComp :: (Integer -> IO()) -> IO()
longComp save = let loop n = save (fac n) >> loop (n + 1)
in loop 0
main :: IO()
main = tmo 10000 longComp >>= print
Die Funktion tmo
wird als erstes Argument übergeben eine IO
Aktion, mit der Zwischenergebnisse gespeichert werden können. Wenn es eine Zeitüberschreitung erhält, wird das letzte gespeicherte Ergebnis zurückgegeben. Das Ergebnis wird in WHNF konvertiert, sodass die tatsächliche Berechnung in dem Thread erfolgt, der das Ergebnis speichert, und nicht in dem Prozess, der es verarbeitet, wenn es von tmo
zurückgegeben wird.
In dieser Variante muss die an tmo
übergebene Funktion ihre Ausgabe speichern, sie kann sie nicht zurückgeben. Aber es wäre einfach, es zu ändern, so dass seine Signatur (a -> IO()) -> IO a
wäre.
Wenn Sie die Dinge reiner halten wollen, würde ich vorschlagen, Ihre eigene Monade zu erstellen, die diese Idee kapselt, ohne IO
herauszulassen.
Update: Beachten Sie, dass:
Es gibt keine Garantie dafür, dass die Ausnahme sofort ausgeliefert werden, obwohl die Laufzeit zu gewährleisten, wird sich bemühen, dass willkürliche Verzögerungen nicht auftreten.In GHC kann eine Ausnahme nur ausgelöst werden, wenn ein Thread einen sicheren Punkt erreicht, wo ein sicherer Punkt ist wo Speicherzuweisung auftritt. Einige Schleifen nicht führen keine Speicherzuweisung innerhalb der Schleife und daher kann nicht durch eine throwTo unterbrochen werden.
(aus der Dokumentation von throwTo). Im obigen Beispiel belegt fac
keinen Speicher und wird daher bei großen Nummern nicht sofort unterbrochen.
Update: Ich habe eine kleine Bibliothek basierend auf diesen Ideen erstellt, die Monaden für Berechnungen definiert, die Teilergebnisse zurückgeben können, bevor die letzte zurückgegeben wird oder bei einer Zeitüberschreitung. Siehe https://github.com/ppetr/timeout-with-results
Verwenden Sie [timeout] (http://www.haskell.org/hoogle/?hoogle=timeout). – leftaroundabout