Sharing mvar between threads
I am trying to create a program that prints arrows until the user presses the enter button (see code below).
The problem is, when I hit enter, I see the line "stop" in the console, but it doesn't change the value of m in the outputArrows function.
How can I share my status?
import Control.Concurrent
import Control.Concurrent.Async
import Control.Monad
waitForInput m = do
getLine
putStrLn "stop"
putMVar m True
outputArrows m = do
stop <- readMVar m
unless stop $ do
threadDelay 1000000
putStr ">"
outputArrows m
main = do
m <- newMVar False
th1 <- async (waitForInput m)
th2 <- async (outputArrows m)
wait th1
wait th2
source to share
Yours putMVar
does not actually put the new value in MVar
, but blocks indefinitely. MVars are like boxes that can only hold one value. If you want to replace a value, you must first take out the old value.
If you don't need to block the MVar behavior, you should just use the regular one, IORef
or perhaps TVar
if you need to make sure more complex operations are done atomically.
source to share
You should use swapMVar
instead putMVar
. As @shang mentioned, it putMVar
blocks until MVar
empty, so it putMVar
never ends:
waitForInput m = do
getLine
putStrLn "stop"
swapMVar m True
Alternatively, you can simply use empty MVar ()
as a boolean flag:
waitForInput :: MVar () -> IO ()
waitForInput m = do
getLine
putStrLn "stop"
putMVar m ()
outputArrows :: MVar () -> IO ()
outputArrows m = do
running <- isEmptyMVar m
when running $ do
threadDelay 1000000
putStr ">"
outputArrows m
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
m <- newEmptyMVar
th1 <- async (waitForInput m)
th2 <- async (outputArrows m)
wait th1
wait th2
source to share