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