2014-08-31 7 views
5

ich einige Experimente mit Nebenläufigkeit und Speichern Sichtbarkeit zu tun und lief in dieses seltsame Verhalten (siehe Kommentare inline):Verästelter IOREF Leserfunktion scheint Haupt-Thread zum Stillstand

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 

ich vielleicht erwartet hatte die writeIORef nicht zu sein sichtbar für den Unterthread, aber nicht für den Hauptthread, um (scheinbar) einfach anzuhalten.

auf ghc Zusammengestellt 7.8.3

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

und laufen mit

./visibility +RTS -N 

Was hier geschieht?

EDIT: Also meine Maschine hat zwei echte Kerne und zwei Hyperthreading-Kerne, so mit +RTS -N GHC sieht 4 Möglichkeiten. Per Gabriel Gonzalez Antwort habe ich versucht, die folgende zu sehen, ob vielleicht wurde der Scheduler auf dem gleichen physischen Prozessor beiden Fäden setzen:

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 

Antwort

3

ghc setzt nur an Fäden sicher Punkte gut definiert, die nur sind, wenn der Speicher zugeordnet ist, . Ich glaube, dass dein gegabelter Thread niemals Speicher zuweist, also gibt er niemals die Kontrolle an andere Threads ab. Daher wird Ihr Hauptthread nie fortgesetzt, sobald der Compiler den gegabelten Thread (irgendwann in der Mitte Ihrer threadDelay) plant.

Weitere Informationen zu sicheren Punkten here finden Sie im Abschnitt "Leichte Gewinde und Parallelität".

Bearbeiten: Wie Thomas erwähnt, können Sie Control.Concurrent.yield verwenden, um die Kontrolle explizit aufzugeben, wenn Sie auf Situationen wie diese stoßen.

+2

Genau. Eine Lösung besteht darin, die "Ausbeute" zu verwenden, um Blöcke ohne Zuweisungen oder andere solche Punkte aufzuteilen. Also für das hektische Warten auf die Frage hätten wir '... sonst gib >> f r'. Offensichtlich sind Busy Loops generell eine schlechte Idee. Eine Alternative besteht darin, "MVar" und "takeMVar" zur Signalisierung zu verwenden. –

+0

Sorry, vielleicht bin ich dicht ... Warum sollte ich den gegabelten Thread brauchen, um die Kontrolle zurück zum Haupt-Thread zu geben, damit 'putStrLn" writeIORef "' läuft? Ich laufe mit '+ RTS -N ', kompiliert mit' -hreaded'; sollen die beiden Threads nicht gleichzeitig laufen? – jberryman

+0

Bitte sehen Sie meine Bearbeitung, auch – jberryman