Jeden pomysł: Kombajny timeout
(sugerowane przez @MathematicalOrchid) ze zmiennymi modyfikowalnych z SafeSemaphore do przechowywania wartości pośrednich każdym razem, gdy proces oblicza częściową wynik:
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
Funkcja przekazane tmo
pobiera jako pierwszy argument akcja, której może użyć do zapisania wyników pośrednich. Jeśli zostanie przekroczony limit czasu, zwrócony zostanie ostatni zapisany wynik. Wynik jest konwertowany na wartość WHNF, tak aby rzeczywiste obliczenia odbywały się w wątku, który zapisuje wynik, a nie w tym, który przetwarza go po powrocie z tmo
.
W tym wariancie funkcja przekazana do tmo
musi zapisać wynik, ale nie może go zwrócić. Ale byłoby łatwo zmodyfikować tak, aby jego podpis był (a -> IO()) -> IO a
.
Jeśli chcesz zachować czystość, proponuję stworzyć własną monadę, która otoczy ten pomysł, nie pozwalając na to, by IO
się skończył.
Aktualizacja: zauważyć, że:
Nie ma gwarancji, że wyjątek zostanie dostarczony natychmiast, chociaż runtime będzie dążyć do zapewnienia, że arbitralne opóźnienia nie występują.W GHC wyjątek można podnieść tylko wtedy, gdy wątek osiągnie bezpieczny punkt, , gdzie bezpiecznym punktem jest miejsce przydzielania pamięci. Niektóre pętle nie wykonują żadnej alokacji pamięci wewnątrz pętli, a zatem nie mogą być przerywane za pomocą polecenia throwTo.
(z dokumentacji throwTo). W powyższym przykładzie fac
nie przydziela żadnej pamięci, więc dla dużych liczb nie zostanie natychmiast przerwana.
Aktualizacja: stworzyłem małą bibliotekę na podstawie tych pomysłów, które definiuje monads obliczeń, które mogą powrócić częściowych wyników przed zwróceniem końcowy jednego lub umiera na timeout. Patrz: https://github.com/ppetr/timeout-with-results
Użyj [limitu czasu] (http://www.haskell.org/hoogle/?hoogle=timeout). – leftaroundabout