수색…


비고

하스켈에서 동시 및 병렬 프로그래밍에 대해 학습 할 수있는 좋은 자료는 다음과 같습니다.

`forkIO`로 쓰레드 스폰하기

하스켈은 여러 형태의 동시성을 지원하며 가장 명백한 것은 forkIO 사용하여 스레드를 포크하는 forkIO 입니다.

함수 forkIO :: IO () -> IO ThreadIdIO 작업을 수행하고 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. 스레드 1 읽기 ma 와 블록 ma
  2. 스레드 2는 mb 읽고 따라서 mb 를 차단합니다.

이제 스레드 1은 mb 를 읽지 못합니다. 스레드 2는 스레드를 차단했으며 스레드 2는 스레드 1이 스레드를 차단 ma 를 읽을 수 없습니다. 고전적인 교착 상태!

소프트웨어 트랜잭션 메모리가있는 원자 블록

하스켈의 또 다른 강력하고 성숙한 동시성 도구는 소프트웨어 트랜잭션 메모리 (Software Transactional Memory)로, 여러 스레드가 하나의 변수 유형 TVar a 원자 적 방식으로 쓸 수있게합니다.

TVar aSTM 모나드와 연관된 주요 유형이며 트랜잭션 변수를 나타냅니다. 그것들은 MVarMVar 하지만 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


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow