2014-08-31 13 views
5

stavo facendo alcuni esperimenti con la concorrenza e la memoria visibilità e sono imbattuto in questo strano comportamento (vedi commenti in linea):funzione di lettore Forked IOREF sembra stallo thread principale

module Main 
    where 

import Data.IORef 
import Control.Concurrent 
import System.CPUTime 

import System.IO 

main = do 
    hSetBuffering stdout NoBuffering 

    r <- newIORef False 
    putStrLn "forking..." -- PRINTED 
    forkIO $ f r 
    threadDelay 1000000 

    putStrLn "writeIORef" -- NEVER PRINTED 
    writeIORef r True 

    threadDelay maxBound 

f :: IORef Bool -> IO() 
f r = readIORef r >>= \b-> if b then print "NEVER PRINTED" else f r 

mi aspettavo forse il writeIORef non essere visibile al thread figlio, ma non per il thread principale a semplicemente (apparentemente) stallo.

compilato su GHC 7.8.3

cabal exec ghc -- --make -fforce-recomp -O2 -threaded visibility.hs 

e correre con

./visibility +RTS -N 

cosa sta succedendo qui?

EDIT: Così la mia macchina ha due nuclei reali e due core hyperthreading, quindi con +RTS -N GHC vede 4 capacità. Per risposta di Gabriel Gonzalez Ho provato il seguente per vedere se magari lo scheduler stava mettendo entrambi i fili sullo stesso processore fisico:

module Main 
    where 

import Data.IORef 
import Control.Concurrent  
import GHC.Conc(threadCapability,myThreadId,forkOn) 

main = do  
    r <- newIORef False 
    putStrLn "going..." 

    (cap,_) <- threadCapability =<< myThreadId 
    forkOn (cap+1) $ f r     -- TRIED cap+1, +2, +3.... 
    threadDelay 1000000 

    putStrLn "writeIORef"     -- BUT THIS STILL NEVER RUNS 
    writeIORef r True 

    threadDelay maxBound 

f :: IORef Bool -> IO() 
f r = readIORef r >>= \b-> if b then print "A" else f r 

risposta

3

ghc sospende solo fili in ben definiti punti sicuri, che sono solo quando la memoria si alloca . Credo che il tuo thread biforcuto non assegni mai memoria, quindi non cede mai il controllo ad altri thread. Pertanto, il thread principale non progredisce mai una volta che il compilatore pianifica il thread biforcuto (a volte nel mezzo di threadDelay).

È possibile ottenere ulteriori informazioni sui punti di sicurezza here nella sezione "Fili leggeri e parallelismo".

Modifica: come menzionato da Thomas, è possibile utilizzare Control.Concurrent.yield per abbandonare esplicitamente il controllo quando si verificano situazioni come queste.

+2

Esattamente. Una soluzione è usare 'yield' per suddividere i blocchi senza allocazioni o altri punti di questo tipo. Quindi per l'attesa impegnata nella domanda avremmo '... else yield >> f r'. Ovviamente i loop occupati sono generalmente una cattiva idea, in primo luogo. Un'alternativa è usare 'MVar' e' takeMVar' per la segnalazione invece. –

+0

Scusate, forse sono denso ... perché dovrei aver bisogno che il thread biforcuto restituisca il controllo al thread principale in modo che "putStrLn" writeIORef "' venga eseguito? Sto correndo con '+ RTS -N', compilato con' -threaded'; i due thread non dovrebbero essere eseguiti simultaneamente? – jberryman

+0

Vedi anche la mia modifica, – jberryman

Problemi correlati