Quiero escribir un programa cuyo hilo principal bifurca un nuevo hilo para el cálculo y espera a que termine por un período de tiempo. Si el hilo secundario no termina en un tiempo dado, se agota el tiempo de espera y se elimina. Tengo el siguiente código para esto.
import Control.Concurrent
fibs :: Int -> Int
fibs 0 = 0
fibs 1 = 1
fibs n = fibs (n-1) + fibs (n-2)
main = do
mvar <- newEmptyMVar
tid <- forkIO $ do
threadDelay (1 * 1000 * 1000)
putMVar mvar Nothing
tid' <- forkIO $ do
if fibs 1234 == 100
then putStrLn "Incorrect answer" >> putMVar mvar (Just False)
else putStrLn "Maybe correct answer" >> putMVar mvar (Just True)
putStrLn "Waiting for result or timeout"
result <- takeMVar mvar
killThread tid
killThread tid'
Compilé el programa anterior con ghc -O2 Test.hsy lo ghc -O2 -threaded Test.hsejecuté, pero en ambos casos el programa simplemente se cuelga sin imprimir nada ni salir. Si agrego un threadDelay (2 * 1000 * 1000)al hilo de cálculo antes del ifbloque, el programa funciona como se esperaba y termina después de un segundo, ya que el hilo del temporizador puede llenar el mvar.
¿Por qué el enhebrado no funciona como esperaba?
multithreading
haskell
concurrency
timeout
blocking
Tipo al azar
fuente
fuente

MVarindican que es susceptible a las condiciones de carrera. Tomaría esa nota en serio.MVardisciplina me parece bien aquí.+RTS -N? consulte wiki.haskell.org/Concurrency para obtener más informaciónRespuestas:
GHC utiliza una especie de híbrido de multitarea cooperativa y preventiva en su implementación de concurrencia.
En el nivel de Haskell, parece preventivo porque los subprocesos no necesitan ceder explícitamente y pueden verse interrumpidos por el tiempo de ejecución en cualquier momento. Pero en el nivel de tiempo de ejecución, los subprocesos "ceden" cada vez que asignan memoria. Dado que casi todos los hilos de Haskell se asignan constantemente, esto generalmente funciona bastante bien.
Sin embargo, si un cálculo en particular puede optimizarse en un código que no se asigna, puede dejar de cooperar en el nivel de tiempo de ejecución y, por lo tanto, no tener prioridad en el nivel de Haskell. Como señaló @Carl, en realidad es la
-fomit-yieldsbandera, lo que implica-O2que permite que esto suceda:Obviamente, en el tiempo de ejecución de un solo subproceso (sin
-threadedmarca), esto significa que un subproceso puede eliminar por completo todos los demás subprocesos. Menos obvio, lo mismo puede suceder incluso si compila-threadedy usa+RTS -Nopciones. El problema es que un subproceso no cooperativo puede privar al programador de tiempo de ejecución . Si en algún momento el subproceso no cooperativo es el único subproceso programado actualmente para ejecutarse, se volverá ininterrumpible y el programador nunca se volverá a ejecutar para considerar la programación de subprocesos adicionales, incluso si pudieran ejecutarse en otros subprocesos O / S.Si solo está tratando de probar algunas cosas, cambie la firma de
fibafib :: Integer -> Integer. ComoIntegercausa la asignación, todo comenzará a funcionar nuevamente (con o sin-threaded).Si se encuentra con este problema en código real , la solución más fácil, con diferencia, es la sugerida por @Carl: si necesita garantizar la capacidad de interrupción de los subprocesos, se debe compilar el código
-fno-omit-yields, lo que mantiene las llamadas del planificador en código no asignado . Según la documentación, esto aumenta los tamaños binarios; Supongo que también conlleva una pequeña penalización de rendimiento.Alternativamente, si el cálculo ya está dentro
IO, entonces explícitamenteyielden el bucle optimizado puede ser un buen enfoque. Para un cómputo puro, puede convertirlo a IO yyield, aunque generalmente puede encontrar una manera simple de introducir una asignación nuevamente. En la mayoría de las situaciones realistas, habrá una manera de introducir solo unos "pocos"yieldo asignaciones, lo suficiente para que el hilo responda nuevamente, pero no lo suficiente como para afectar seriamente el rendimiento. (Por ejemplo, si tiene algunos bucles recursivos anidados,yieldo si fuerza una asignación en el bucle más externo).fuente
-fno-omit-yields, ya que-O2implica-fomit-yields. downloads.haskell.org/~ghc/latest/docs/html/users_guide/…