Haskell Language
동시성
수색…
비고
하스켈에서 동시 및 병렬 프로그래밍에 대해 학습 할 수있는 좋은 자료는 다음과 같습니다.
`forkIO`로 쓰레드 스폰하기
하스켈은 여러 형태의 동시성을 지원하며 가장 명백한 것은 forkIO
사용하여 스레드를 포크하는 forkIO
입니다.
함수 forkIO :: IO () -> IO ThreadId
는 IO
작업을 수행하고 ThreadId
반환하고 작업은 백그라운드에서 실행됩니다.
ghci
사용하여 매우 간결하게 설명 할 수 있습니다.
Prelude Control.Concurrent> forkIO $ (print . sum) [1..100000000]
ThreadId 290
Prelude Control.Concurrent> forkIO $ print "hi!"
"hi!"
-- some time later....
Prelude Control.Concurrent> 50000005000000
두 가지 작업 모두 백그라운드에서 실행되며 두 번째 작업은 마지막 작업이 끝나기 전에 거의 완료되도록 보장됩니다!
`MVar '로 쓰레드 간 통신하기
MVar a
타입과 Control.Concurrent
함수를 사용하여 스레드간에 정보를 전달하는 것은 매우 쉽습니다.
-
newEmptyMVar :: IO (MVar a)
- 새로운MVar a
만듭니다.MVar a
-
newMVar :: a -> IO (MVar a)
- 지정된 값으로 새MVar
를 만듭니다. -
takeMVar :: MVar a -> IO a
- 주어진MVar
에서 값을 검색하거나 사용할 수있을 때까지 블록 합니다. -
putMVar :: MVar a -> a -> IO ()
-MVar
주어진 값을MVar
비어있을 때까지 차단 합니다.
스레드에서 1에서 1 억까지의 숫자를 합산하고 결과를 기다립니다.
import Control.Concurrent
main = do
m <- newEmptyMVar
forkIO $ putMVar m $ sum [1..10000000]
print =<< takeMVar m -- takeMVar will block 'til m is non-empty!
좀 더 복잡한 데모는 더 많은 입력을 기다리는 동안 백그라운드에서 사용자 입력과 합계를 취하는 것일 수 있습니다.
main2 = loop
where
loop = do
m <- newEmptyMVar
n <- getLine
putStrLn "Calculating. Please wait"
-- In another thread, parse the user input and sum
forkIO $ putMVar m $ sum [1..(read n :: Int)]
-- In another thread, wait 'til the sum's complete then print it
forkIO $ print =<< takeMVar m
loop
앞서 언급했듯이 takeMVar
를 호출하고 MVar
가 비어 있으면 다른 스레드가 MVar
에 무언가를 넣을 때까지 차단되어 식사 철학자 문제 가 발생할 수 있습니다. putMVar
똑같은 일이 일어 putMVar
. 꽉 차 있다면, 그것이 비어있을 때까지 블록됩니다.
다음 기능을 수행하십시오.
concurrent ma mb = do
a <- takeMVar ma
b <- takeMVar mb
putMVar ma a
putMVar mb b
우리는 몇 가지 MVar
두 함수를 실행합니다.
concurrent ma mb -- new thread 1
concurrent mb ma -- new thread 2
일어날 수있는 일은 다음과 같습니다.
- 스레드 1 읽기
ma
와 블록ma
- 스레드 2는
mb
읽고 따라서mb
를 차단합니다.
이제 스레드 1은 mb
를 읽지 못합니다. 스레드 2는 스레드를 차단했으며 스레드 2는 스레드 1이 스레드를 차단 ma
를 읽을 수 없습니다. 고전적인 교착 상태!
소프트웨어 트랜잭션 메모리가있는 원자 블록
하스켈의 또 다른 강력하고 성숙한 동시성 도구는 소프트웨어 트랜잭션 메모리 (Software Transactional Memory)로, 여러 스레드가 하나의 변수 유형 TVar a
원자 적 방식으로 쓸 수있게합니다.
TVar a
는 STM
모나드와 연관된 주요 유형이며 트랜잭션 변수를 나타냅니다. 그것들은 MVar
와 MVar
하지만 STM
모나드 내에서 다음과 같은 기능을 통해 사용됩니다.
atomically :: STM a -> IO a
원자 적으로 일련의 STM 작업을 수행하십시오.
readTVar :: TVar a -> STM a
TVar
의 값을 읽습니다. 예 :
value <- readTVar t
writeTVar :: TVar a -> a -> STM ()
주어진 TVar
값을 쓰 TVar
.
t <- newTVar Nothing writeTVar t (Just "Hello")
이 예제는 하스켈 위키에서 가져온 것입니다 :
import Control.Monad import Control.Concurrent import Control.Concurrent.STM main = do -- Initialise a new TVar shared <- atomically $ newTVar 0 -- Read the value before <- atomRead shared putStrLn $ "Before: " ++ show before forkIO $ 25 `timesDo` (dispVar shared >> milliSleep 20) forkIO $ 10 `timesDo` (appV ((+) 2) shared >> milliSleep 50) forkIO $ 20 `timesDo` (appV pred shared >> milliSleep 25) milliSleep 800 after <- atomRead shared putStrLn $ "After: " ++ show after where timesDo = replicateM_ milliSleep = threadDelay . (*) 1000 atomRead = atomically . readTVar dispVar x = atomRead x >>= print appV fn x = atomically $ readTVar x >>= writeTVar x . fn