Suche…


Bemerkungen

Gute Ressourcen für das Erlernen der gleichzeitigen und parallelen Programmierung in Haskell sind

Laichgewinde mit `forkIO`

Haskell unterstützt viele Formen der Parallelität. Die offensichtlichste forkIO einen Thread mithilfe von forkIO .

Die Funktion forkIO :: IO () -> IO ThreadId führt eine IO Aktion aus und gibt ihre ThreadId , ThreadId die Aktion im Hintergrund ausgeführt wird.

Mit ghci können wir das kurz und knapp 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

Beide Aktionen werden im Hintergrund ausgeführt, und die zweite wird fast garantiert vor der letzten beendet!

Kommunikation zwischen Threads mit "MVar"

Es ist sehr einfach, Informationen zwischen Threads mit dem Typ MVar a und den zugehörigen Funktionen in Control.Concurrent :

  • newEmptyMVar :: IO (MVar a) - erstellt eine neue MVar a
  • newMVar :: a -> IO (MVar a) - erstellt eine neue MVar mit dem angegebenen Wert
  • takeMVar :: MVar a -> IO a - ruft den Wert von der angegebenen MVar oder blockiert, bis einer verfügbar ist
  • putMVar :: MVar a -> a -> IO () - setzt den angegebenen Wert in die MVar oder blockiert, bis er leer ist

Summieren Sie die Zahlen von 1 bis 100 Millionen in einem Thread und warten Sie auf das Ergebnis:

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

Eine komplexere Demonstration könnte darin bestehen, die Benutzereingaben und die Summe im Hintergrund aufzunehmen, während auf weitere Eingaben gewartet wird:

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

Wenn Sie takeMVar und die MVar leer ist, wird sie blockiert, bis ein anderer Thread etwas in die MVar , was zu einem Problem mit Speisesachen führen kann . Dasselbe passiert mit putMVar : Wenn es voll ist, wird es blockiert, bis es leer ist!

Nehmen Sie die folgende Funktion vor:

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

Wir führen die beiden Funktionen mit einigen MVar s aus

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

Was passieren könnte ist:

  1. Thread 1 liest ma und blockiert ma
  2. Thread 2 liest mb und blockiert somit mb

Fädeln Sie jetzt 1 kann nicht gelesen mb als Thread 2 es blockiert wurde, und 2 Thread kann nicht lesen ma als Thread 1 es blockiert hat. Ein klassischer Deadlock!

Atomblöcke mit Software-Transaktionsspeicher

Ein weiteres leistungsfähiges und ausgereiftes Parallelitätswerkzeug in Haskell ist Software Transactional Memory, mit dem mehrere Threads TVar a in eine einzige Variable vom Typ TVar a schreiben können.

TVar a ist der Haupttyp der STM Monade und steht für Transaktionsvariable. Sie werden ähnlich wie MVar aber innerhalb der STM Monade durch die folgenden Funktionen verwendet:

atomically :: STM a -> IO a

Führen Sie eine Reihe von STM-Aktionen atomar aus.

readTVar :: TVar a -> STM a

Lesen Sie den TVar -Wert, zB:

value <- readTVar t

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

Schreibe einen Wert in den angegebenen TVar .

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

Dieses Beispiel stammt aus dem 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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow