summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2015-12-23 20:45:54 +0100
committerGregor Kleen <gkleen@yggdrasil.li>2015-12-23 20:45:54 +0100
commit1d27f7b9aac4058f28aa8e56cb112ad1018614d7 (patch)
tree0194af4bf77ec1bcfb8fb7ff4f698230b570fb61
parent5d9a2f27772eabdf3f5d0a53a533773925197f12 (diff)
downloaduni-1d27f7b9aac4058f28aa8e56cb112ad1018614d7.tar
uni-1d27f7b9aac4058f28aa8e56cb112ad1018614d7.tar.gz
uni-1d27f7b9aac4058f28aa8e56cb112ad1018614d7.tar.bz2
uni-1d27f7b9aac4058f28aa8e56cb112ad1018614d7.tar.xz
uni-1d27f7b9aac4058f28aa8e56cb112ad1018614d7.zip
FFP 09
-rw-r--r--ws2015/ffp/blaetter/09/FFP_U09_Concurrent.hs328
1 files changed, 328 insertions, 0 deletions
diff --git a/ws2015/ffp/blaetter/09/FFP_U09_Concurrent.hs b/ws2015/ffp/blaetter/09/FFP_U09_Concurrent.hs
new file mode 100644
index 0000000..99026c0
--- /dev/null
+++ b/ws2015/ffp/blaetter/09/FFP_U09_Concurrent.hs
@@ -0,0 +1,328 @@
1-- Fortgeschrittene Funktionale Programmierung,
2-- LMU, TCS, Wintersemester 2015/16
3
4-- Steffen Jost, Alexander Isenko
5--
6-- Übungsblatt 09. 16.12.2015
7--
8-- Thema: Ausnahmen & Nebenläufigkeit
9--
10-- Hinweis:
11-- Für diese Übung ist ein Rechner mit mehreren Kernen
12-- nützlich, aber nicht zwingend notwendig.
13-- Wenn Sie möchten, dann Benutzen Sie einfach einen
14-- Mehrkern-Rechner am CIP-Pool der Informatik. Hinweise
15-- zum Remote-Login am CIP-Pool finden Sie am Anfang des
16-- vorherigen Übungsblattes.
17--
18--
19-- Anweisungen:
20--
21-- Gehen Sie diese Datei durch und bearbeiten Sie
22-- alle Vorkommen von undefined bzw. die mit -- !!! TODO !!!
23-- markierten Stellen. Testen Sie Ihre Lösungen mit GHC!
24--
25
26
27import Data.List
28import Data.Typeable
29import Control.Exception
30import Control.Monad
31import Control.Concurrent
32import Control.DeepSeq
33
34import Data.Ord (comparing)
35
36import System.Environment (getArgs)
37
38-- Zum Umschalten zwischen den Aufgaben umkommentieren,
39-- oder besser die Aufgaben in einzelne Dateien auftrennen.
40main = main' [ ("1", main_A8_1)
41 , ("2", main_A8_2)
42 , ("3", main_A8_3)
43 ]
44
45main' :: [(String, IO ())] -> IO ()
46main' as = do
47 args <- getArgs
48 let
49 handler (x, a)
50 | x `elem` args = a
51 | otherwise = return ()
52 mapM_ handler as
53
54
55-- A8-1 Ausnahmen
56--
57-- a)
58-- Definieren Sie eine Funktion "myHead",
59-- welche wie "head" aus der Standardbibliothek funktioniert,
60-- aber stattdessen eine von Ihnen selbst-definierte
61-- Ausnahme wirft.
62--
63
64data EmptyListException = EmptyListException
65 deriving (Show, Typeable)
66
67instance Exception EmptyListException
68
69myHead :: [a] -> a
70myHead [] = throw EmptyListException
71myHead (a:_) = a
72
73main_A8_1 :: IO ()
74main_A8_1 = do
75 putStrLn "Jetzt sollten drei Werte kommen, aber keine Exception:"
76 print $ myHead [1,2,undefined] -- Zeile nicht ändern!
77 h1 <- mySafeHead [3,4,undefined] -- Zeile nicht ändern!
78 print h1
79 h2 <- mySafeHead empty
80 print h2
81 putStrLn "Jetzt sollte gleich eine benutzerdefinierte Exception kommen:"
82 print $ myHead empty
83 where
84 empty = [] :: [Int] -- Typangabe für leere Liste,
85 -- damit Show-Instance festgelegt wird.
86 -- HINWEIS:
87 -- Das ist gar nicht albern, denn
88 -- "print ([] :: String)" soll ja etwas
89 -- anderes ausgeben als
90 -- "print ([] :: [Int])"!
91
92
93-- b)
94-- Implementieren Sie "mySafeHead" unter Verwendung
95-- von "myHead", indem Sie die Ausnahme abfangen und behandeln.
96--
97-- Hinweis:
98-- Ausnahmen können nur innerhalb der IO-Monade gefangen werden.
99
100
101mySafeHead :: [a] -> IO (Maybe a)
102-- mySafeHead [] = return Nothing -- Gilt hier nicht als Lösung! Zu Übungszwecken bitte myHead mit catch/try einsetzen!
103mySafeHead = handle (\EmptyListException -> return Nothing) . (fmap Just) . evaluate . myHead
104
105
106
107
108-- A8-2 Paralleles Rechnen mit Nebenläufigkeit
109--
110-- Auch wenn es eigentlich nicht der Sinn der Sache ist:
111-- Nebenläufigkeit kann ebenfalls dazu verwendet werden,
112-- die Ausführung eines Programmes durch Ausnutzung mehrerer
113-- Prozessorkerne zu beschleunigen.
114--
115-- Das folgende Programm führt vier unterschiedlich aufwändige
116-- Berechnungen durch (Aufgabenstellung folgt weiter unten):
117
118main_A8_2 :: IO ()
119main_A8_2 = main' [ ("seq", main_A8_2_seq)
120 , ("a", main_A8_2_a)
121 , ("b", main_A8_2_b)
122 ]
123
124type Task = Integer -> [String]
125
126main_A8_2_seq :: IO ()
127main_A8_2_seq = do
128 putStrLn "Ein paar interessante Fakten:"
129 putStrLn " "
130 mapM_ putStrLn $ taskA nA
131 mapM_ putStrLn $ taskB nB
132 mapM_ putStrLn $ taskC nC
133 mapM_ putStrLn $ taskD nD
134 putStrLn " "
135 putStrLn "Höchst interessant, oder?"
136
137
138-- Hilfsfunktionen, zur Erzeugung von Rechnenlast,
139-- zur Lösung der Aufgabe sind diese unbedeutend.
140--
141-- HINWEIS:
142-- Die Werte sind zur Ausführung mit GHC -O2 auf aktueller Hardware gedacht
143-- Bei Ausführung in GHCI sind die Werte anzupassen (halbieren?).
144-- Idealerweise sollte jeder Task ca. 10 Sekunden Laufzeit benötigen.
145
146nA = 14
147
148taskA :: Task
149taskA n = ["* Ein Hanoi-Turm der Höhe " ++ (show n)
150 ," braucht genau " ++ (show steps) ++ " Schritte zum versetzen." ]
151 where
152 steps = length $ hanoi n 1 3
153
154hanoi :: (Num a, Eq a) => a -> a -> a -> [(a,a)]
155hanoi 1 i j = [(i,j)]
156hanoi n i j =
157 hanoi n' i otherTower
158 ++ [(i,j)]
159 ++ hanoi n' otherTower j
160 where
161 n' = n-1
162 otherTower = 1+2+3-i-j
163
164nB = 30
165
166taskB :: Task
167taskB n = ["* Die " ++ (show n) ++ ". Fibonacci-Zahl"
168 ," lautet: " ++ (show fn) ]
169 where
170 fn = fib n
171
172fib :: (Ord a, Num a, Num b) => a -> b
173fib 0 = 0 -- absichtlich langsame Berechnung
174fib n | n <= 1 = 1
175 | otherwise = fib (n-1) + fib (n-2)
176
177nC = 11728
178
179taskC :: Task
180taskC n = ["* Die Fakultät von " ++ (show n)
181 ," ist eine Zahl mit " ++ (show l) ++ " Stellen." ]
182 where
183 fn = product [1..n]
184 l = length $ show fn
185
186nD = 279
187
188taskD :: Task
189taskD n = ["* Die Anzahl der Collatz-Schritte von " ++ (show n) ++ " bis 1 "
190 ," beträgt genau " ++ (show cn)]
191 where
192 cn = numCSteps n
193
194
195collatzStep :: Integral a => a -> a
196collatzStep n
197 | even n = n `div` 2
198 | otherwise = 3*n+1
199
200numCSteps :: Integral a => a -> a
201numCSteps n = ncs 0 n
202 where -- Hinweis: Es ist unbekannt, ob es immer terminiert.
203 ncs steps 1 = steps
204 ncs steps m = ncs (succ steps) (collatzStep m)
205
206
207-- a)
208-- Verändern Sie das gegebene Hauptprogramm so, dass
209-- die vier Ergebnisse so schnell wie möglich ausgegeben werden.
210-- Das am schnellsten zu berechnende Ergebnis soll also zuerst ausgegeben werden,
211-- das am langsamsten zu berechnende Ergebnis am Ende.
212-- Die einzelnen Zeilen einer Aufgabe sollen auch zusammen ausgegeben werden!
213--
214-- Hinweis:
215-- Sie sehen zwar keine Beschleunigung, wenn Sie nur einen Prozessorkern
216-- zur Verfügung haben, aber Sie sehen trotzdem, dass
217-- die leichteste Aufgabe (taskD) als erste ausgegeben wird,
218-- wenn Sie die Aufgabe richtig gelöst haben!
219
220main_A8_2_a :: IO ()
221main_A8_2_a = do
222 putStrLn "Ein paar interessante Fakten:"
223 putStrLn " "
224 runPar [ taskA nA
225 , taskB nB
226 , taskC nC
227 , taskD nD
228 ]
229 putStrLn " "
230 putStrLn "Höchst interessant, oder?"
231 where
232 runPar as = do
233 rChan <- newChan
234 mapM_ (forkIO . (writeChan rChan $!!)) as
235 replicateM_ (length as) (readChan rChan >>= mapM_ putStrLn)
236
237
238-- b)
239-- Verändern Sie das Hauptprogramm so, dass die Berechnungen
240-- zwar parallel ausgeführt werden, aber das die gesamte Ausgabe
241-- in einem Schritt nach Ende der langsamsten Berechnung erfolgt,
242-- in der gleichen Reihenfolge des ursprünglichen Programmes.
243
244main_A8_2_b :: IO ()
245main_A8_2_b = do
246 putStrLn "Ein paar interessante Fakten:"
247 putStrLn " "
248 runPar [ taskA nA
249 , taskB nB
250 , taskC nC
251 , taskD nD
252 ]
253 putStrLn " "
254 putStrLn "Höchst interessant, oder?"
255 where
256 runPar as = do
257 rChan <- newChan
258 mapM_ (forkIO . (writeChan rChan $!!)) as'
259 rs <- replicateM (length as) (readChan rChan)
260 mapM_ putStrLn . join $ map snd $ sortBy (comparing fst) rs
261 where
262 as' = zip ([0..] :: [Integer]) as
263
264
265
266-- A8-3 Dead Locks und Race-Conditions
267--
268-- Was passiert in folgendem Programm?
269-- Wieso kommt es zu einem Dead-Lock?
270-- Überlegen Sie sich Möglichkeiten, dies zu vermeiden!
271
272{-
273 Neither `md` nor `workerC` ever actually get used
274
275 It suffices to show that a scheduling exists for which
276 a deadlock occurs.
277
278 Consider: A.1, B.1, B'.1, A.2, B.2, B'.2
279
280
281 To prevent deadlocks with only minimal changes to semantics
282 consider using one `MVar (Integer, Integer, Integer)`.
283
284 However in the authors opinion considerable changes to the
285 semantics are preferable.
286-}
287
288main_A8_3 :: IO ()
289main_A8_3 = do
290 ma <- newMVar 0
291 mb <- newMVar 0
292 mc <- newMVar 0
293 md <- newMVar 0
294 forkIO $ forever $ workerA ma mb -- thread A
295 forkIO $ forever $ workerB mb mc -- thread B
296 forkIO $ forever $ workerB mc ma -- thread B'
297 replicateM_ 1000 $ do
298 a <- readMVar ma
299 b <- readMVar mb
300 c <- readMVar mc
301 d <- readMVar md
302 print (a,b,c,d)
303 threadDelay 4000
304
305workerA :: MVar Integer -> MVar Integer -> IO ()
306workerA mx my = do
307 x <- takeMVar mx -- A.1
308 y <- takeMVar my -- A.2
309 let x' = x+1
310 let y' = y-1
311 putMVar my $! y'
312 putMVar mx $! x'
313
314workerB :: MVar Integer -> MVar Integer -> IO ()
315workerB mx my = do
316 x <- takeMVar mx -- B.1 / B'.1
317 y <- takeMVar my -- B.2 / B'.2
318 putMVar mx $! collatzStep x
319 putMVar my $! collatzStep y
320
321workerC :: MVar Integer -> MVar Integer -> IO ()
322workerC mx my = do
323 x <- takeMVar mx
324 let x' = if x < 42 then fib x else 0
325 putMVar mx $! x'
326 y <- takeMVar my
327 let y' = y + x'
328 putMVar my $! y'