Sök…


Anmärkningar

Bra resurser för att lära sig om samtidig och parallell programmering i Haskell är:

Lektrådar med `forkIO`

Haskell stöder många former av samtidighet och det mest uppenbara är att smida en tråd med forkIO .

Funktionen forkIO :: IO () -> IO ThreadId gör en IO åtgärd och returnerar sin ThreadId , under tiden kommer åtgärden att köras i bakgrunden.

Vi kan demonstrera detta ganska kortfattat med 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

Båda åtgärderna kommer att köras i bakgrunden, och den andra är nästan garanterad att vara klar före den sista!

Kommunikera mellan trådarna med `MVar`

Det är väldigt lätt att skicka information mellan trådar med MVar a typ och dess tillhörande funktioner i Control.Concurrent :

  • newEmptyMVar :: IO (MVar a) - skapar en ny MVar a
  • newMVar :: a -> IO (MVar a) - skapar en ny MVar med det angivna värdet
  • takeMVar :: MVar a -> IO a - hämtar värdet från den givna MVar , eller blockerar tills ett är tillgängligt
  • putMVar :: MVar a -> a -> IO () - sätter det givna värdet i MVar , eller blockerar tills det är tomt

Låt oss summera siffrorna från 1 till 100 miljoner i en tråd och vänta på resultatet:

import Control.Concurrent
main = do
  m <- newEmptyMVar
  forkIO $ putMVar m $ sum [1..10000000]
  print =<< takeMVar m  -- takeMVar will block 'til m is non-empty!

En mer komplex demonstration kan vara att ta användarinmatning och summa i bakgrunden medan du väntar på mer input:

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

Som tidigare nämnts, om du kallar takeMVar och MVar är tom blockeras den tills en annan tråd sätter något i MVar , vilket kan leda till ett Dining Philosophers Problem . Samma sak händer med putMVar : om det är fullt blockeras det tills det är tomt!

Ta följande funktion:

concurrent ma mb = do
  a <- takeMVar ma
  b <- takeMVar mb
  putMVar ma a
  putMVar mb b

Vi kör de två funktionerna med några MVar

concurrent ma mb     -- new thread 1 
concurrent mb ma     -- new thread 2

Det som kan hända är att:

  1. Tråd 1 läser ma och blockerar ma
  2. Tråd 2 läser mb och blockerar därmed mb

Nu kan tråd 1 inte läsa mb eftersom tråd 2 har blockerat den, och tråd 2 kan inte läsa ma eftersom tråd 1 har blockerat den. Ett klassiskt dödläge!

Atomblock med mjukvara Transaktionsminne

Ett annat kraftfullt och moget samtidighetsverktyg i Haskell är Software Transactionional Memory, som gör det möjligt för flera trådar att skriva till en enda variabel av typen TVar a på ett atomiskt sätt.

TVar a är den huvudtyp som är associerad med STM monaden och står för transaktionsvariabel. De används ungefär som MVar men inom STM monaden genom följande funktioner:

atomically :: STM a -> IO a

Utför en serie STM-åtgärder atomiskt.

readTVar :: TVar a -> STM a

Läs TVar värde, t.ex.:

value <- readTVar t

writeTVar :: TVar a -> a -> STM ()

Skriv ett värde till den givna TVar .

t <- newTVar Nothing
writeTVar t (Just "Hello")

Detta exempel är hämtat från Haskell Wiki:

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow