• Keine Ergebnisse gefunden

Reaktive Programmierung Vorlesung 3 vom 24.04.2019 Nebenläufigkeit: Futures and Promises

N/A
N/A
Protected

Academic year: 2022

Aktie "Reaktive Programmierung Vorlesung 3 vom 24.04.2019 Nebenläufigkeit: Futures and Promises"

Copied!
29
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Reaktive Programmierung Vorlesung 3 vom 24.04.2019 Nebenläufigkeit: Futures and Promises

Christoph Lüth, Martin Ring

Universität Bremen

Sommersemester 2019

(2)

Fahrplan

I Einführung

I Monaden und Monadentransformer I Nebenläufigkeit: Futures and Promises I Aktoren I: Grundlagen

I Aktoren II: Implementation I Meta-Programmierung

I Bidirektionale Programmierung I Reaktive Ströme I

I Reaktive Ströme II

I Funktional-Reaktive Programmierung I Software Transactional Memory I Eventual Consistency

I Robustheit und Entwurfsmuster I

(3)

Inhalt

I Konzepte der Nebenläufigkeit

I Nebenläufigkeit in Scala und Haskell

I Futures and Promises

(4)

Konzepte der

Nebenläufigkeit

(5)

Begrifflichkeiten

I Thread (lightweight process) vs. Prozess Programmiersprache/Betriebssystem Betriebssystem (z.B. Java, Haskell/Linux)

gemeinsamer Speicher getrennter Speicher

Erzeugung billig Erzeugung teuer

mehrere pro Programm einerpro Programm

I Multitasking:

I präemptiv:Kontextwechsel wirderzwungen I kooperativ:Kontextwechsel nurfreiwillig

(6)

Threads in Java

I Erweiterung der KlassenThreadoder Runnable

I Gestartet wird Methoderun () — durch eigene überladen I Starten des Threads durch Aufruf der Methode s t a r t () I Kontextwechsel mit y i e l d ()

I Je nach JVM kooperativoderpräemptiv.

I Synchronisation mitMonitoren(synchronize)

(7)

Threads in Scala

I Scala nutzt das Threadmodell der JVM

I Kein sprachspezifisches Threadmodell

I Daher sind Threads vergleichsweiseteuer.

I Synchronisation auf unterster Ebene durch Monitore (synchronized)

I Bevorzugtes Abstraktionsmodell:Aktoren (dazu später mehr)

(8)

Threads in Haskell: Concurrent Haskell

I SequentiellesHaskell: Reduktion eines Ausdrucks I Auswertung

I NebenläufigesHaskell: Reduktion eines Ausdrucks anmehreren Stellen

I ghcimplementiert Haskell-Threads

I Zeitscheiben (Default 20ms), Kontextwechsel bei Heapallokation I Threaderzeugung und Kontextswitch sindbillig

I Modul Control . Concurrententhält Basisfunktionen I Wenige Basisprimitive, darauf aufbauend Abstraktionen I Synchronisation mit Futures

(9)

Futures

I Futures machen Nebenläufigkeitexplizit I Grundprinzip:

I Ausführung eines Threads wirdverzögert

I Konsument startet erst, wenn Ergebnis vorhanden.

put

Produzent Konsument

Note: Not a UML sequence diagram

Initiator

(10)

Futures in Scala

(11)

Futures in Scala

I Antwort alsCallback:

t r a i t Future[+T] {

def onComplete( f : Try [T] ⇒ Unit ) : Unit def map[U] ( f : T⇒ U) : Future [U]

def flatMap [U] ( f : T⇒ Future [U] ) : Future [U]

def f i l t e r (p : T⇒ Boolean) : Future [T]

}

I map, flatMap , f i l t e r für monadische Notation I Factory-Methode für einfache Erzeugung

I Vordefiniert in scala . concurrent . Future, Beispielimplementation Future . scala

(12)

Beispiel: Robot . s c a l a

I Roboter, kann sich umnPositionen bewegen:

i f (n ≤ 0) this

else i f ( battery > 0) {

Thread . sleep (100∗Random. nextInt (10) ) ; Robot( id , pos+1, battery− 1) .mv(n−1) } else throw new LowBatteryException

def move(n : Int ) : Future [ Robot ] = Future { mv(n) } override def toString = s"Robot#$id at $pos [ battery :

$battery ] "

(13)

Beispiel: Moving the robots

object Examples { def ex1 = {

val robotSwarm = L i s t . range (1 ,6) .map{ i⇒ Robot( i ,0 ,10)}

val moved = robotSwarm .map(_.move(10) ) moved.map(_. onComplete( p r i n t l n ) )

p r i n t l n ("Started moving . . . ")

I 6 Roboter erzeugen, alle um zehn Positionen bewegen.

I Wie lange dauert das?

I 0 Sekunden (nach spät. 10 Sekunden Futures erfüllt) I Was wir verschweigen:ExecutionContext

(14)

Compositional Futures

I Wir können Futures komponieren I “Spekulation auf die Zukunft”

I Beispiel: Roboterbewegung

def ex2 = { val r= Robot(99 , 0 , 20) ; for { r1 ← r .move(3)

r2 ← r1 .move(5) r3 ← r2 .move(2)

I Fehler (Failure) werden propagiert

(15)

Promises

I Promises sind das Gegenstück zu Futures t r a i t Promise {

def complete( r e s u l t : Try [T] ) def success ( r e s u l t : T) def future : Future [T]

}

object Promise {

def apply [T] : Promise [T] = . . . }

I Das Future eines Promises wird durch die completeMethode erfüllt.

(16)

Futures in Haskell

(17)

Concurrent Haskell: Wesentliche Typen und Funktionen

I Jeder Thread hat einen Identifier: abstrakter TypThreadId I Neuen Thread erzeugen:forkIO :: IO()→ IO ThreadId I Thread stoppen: killThread :: ThreadId →IO () I Kontextwechsel: yield :: IO ()

I Eigener Thread:myThreadId :: IO ThreadId I Warten:threadDelay :: Int → IO ()

(18)

Concurrent Haskell — erste Schritte

I Ein einfaches Beispiel:

write :: Char→ IO ()

write c =do putChar c ; write c main :: IO ()

main = do forkIO ( write ’X’ ) ; write ’O’

I Ausgabeghc: (X|O)

(19)

Futures in Haskell: MVars

I Basissynchronisationmechanismusin Concurrent Haskell I Alles andereabgeleitet

I Grundprinzip:

take

put

Produzent Konsument

Note: Not a UML sequence diagram

(20)

Futures in Haskell: MVars

I MVar αist polymorphüber dem Inhalt

I Entwederleeroder gefülltmit Wert vom Typα

I Verhalten beim Lesen und Schreiben:

Zustand vorher: leer gefüllt

Lesen blockiert(bis gefüllt) danach leer Schreiben danach gefüllt blockiert(bis leer) I NB.Aufwecken blockierter ProzesseeinzelninFIFO

(21)

Basisfunktionen MVars

I Neue Variable erzeugen (leer oder gefüllt):

newEmptyMVar :: IO (MVar α) newMVar :: α → IO (MVar α) I Lesen:

takeMVar :: MVar α → IO α I Schreiben:

putMVar :: MVar αα → IO ()

I Es gibt noch weitere (nicht-blockierend lesen/schreiben, Test ob gefüllt,mapetc.)

(22)

Ein einfaches Beispiel: Robots Revisited

data Robot = Robot {id :: Int , pos :: Int , battery :: Int}

I Hauptfunktion: MVar anlegen, nebenläufig Bewegung starten move :: Robot→ Int→ IO (MVar Robot)

move r n =do

m ← newEmptyMVar; forkIO (mv m r n) ; return m I Bewegungsfunktion:

mv :: MVar Robot → Robot→ Int→ IO () mv v r n

| n ≤ 0 = putMVar v r

| otherwise = do

m← randomRIO(0 ,10) ; threadDelay (m∗100000)

mv v r{pos= pos r + 1 , battery= battery r− 1} (n−1)

(23)

Abstraktion von Futures

I AusMVar αkonstruierte Abstraktionen

I Semaphoren (QSemaus Control . Concurrent .QSem):

waitQSem :: QSem → IO () signalQSem :: QSem → IO ()

I SieheSem. hs

I Damit auch synchronizedwie in Java (huzzah!)

I Kanäle (Chan αausControl . Concurrent .Chan):

writeChan :: Chan αα → IO () readChan :: Chan α → IO α

(24)

Asynchrone Ausnahmen

I Ausnahmen unterbrechen den sequentiellen Kontrollfluß I In Verbindung mit Nebenläufigkeitüberraschende Effekte:

m ← newEmptyMVar

forkIO (do {s← takeMVar m; putStrLn s }) threadDelay (100000)

putMVar m ( er ro r "FOO! ")

I In welchem Thread wird die Ausnahme geworfen?

I Wo kann sie gefangen werden?

I Deshalb haben in Scala die Future-Callbacks den Typ:

t r a i t Future[+T] { def onComplete( f : Try [T] ⇒ Unit ) : Unit

(25)

Asynchrone Ausnahmen

I Ausnahmen unterbrechen den sequentiellen Kontrollfluß I In Verbindung mit Nebenläufigkeitüberraschende Effekte:

m ← newEmptyMVar

forkIO (do {s← takeMVar m; putStrLn s }) threadDelay (100000)

putMVar m ( er ro r "FOO! ")

I In welchem Thread wird die Ausnahme geworfen?

I Wo kann sie gefangen werden?

I Deshalb haben in Scala die Future-Callbacks den Typ:

t r a i t Future[+T] { def onComplete( f : Try [T] ⇒ Unit ) : Unit

(26)

Asynchrone Ausnahmen

I Ausnahmen unterbrechen den sequentiellen Kontrollfluß I In Verbindung mit Nebenläufigkeitüberraschende Effekte:

m ← newEmptyMVar

forkIO (do {s← takeMVar m; putStrLn s }) threadDelay (100000)

putMVar m ( er ro r "FOO! ")

I In welchem Thread wird die Ausnahme geworfen?

I Wo kann sie gefangen werden?

I Deshalb haben in Scala die Future-Callbacks den Typ:

t r a i t Future[+T] { def onComplete( f : Try [T] ⇒ Unit ) : Unit

(27)

Explizite Fehlerbehandlung mit Try

I Die Signatur einer Methode verrät nichts über mögliche Fehler:

i f (n ≤ 0) this

else i f ( battery > 0) {

I Try [T] macht Fehler explizit (Materialisierungoder Reifikation):

sealed abstract class Try[+T] {

def flatMap [U] ( f : T⇒ Try [U] ) : Try [U] = this match { case Success (x) ⇒

try f (x) catch { case NonFatal(ex) ⇒ Failure (ex) } case f a i l : Failure ⇒ f a i l }

case class Success [T] ( x : T) extends Try [T]

case class Failure (ex : Throwable) extends Try [ Nothing ] I IstTryeine Monade?

Nein,Try(e) flatMap f 6= f e

(28)

Explizite Fehlerbehandlung mit Try

I Die Signatur einer Methode verrät nichts über mögliche Fehler:

i f (n ≤ 0) this

else i f ( battery > 0) {

I Try [T] macht Fehler explizit (Materialisierungoder Reifikation):

sealed abstract class Try[+T] {

def flatMap [U] ( f : T⇒ Try [U] ) : Try [U] = this match { case Success (x) ⇒

try f (x) catch { case NonFatal(ex) ⇒ Failure (ex) } case f a i l : Failure ⇒ f a i l }

case class Success [T] ( x : T) extends Try [T]

case class Failure (ex : Throwable) extends Try [ Nothing ] I IstTryeine Monade? Nein, Try(e) flatMap f 6= f e

(29)

Zusammenfassung

I Nebenläufigkeit in Scalabasiert auf der JVM:

I Relativ schwergewichtige Threads, Monitore (synchronized)

I Nebenläufigkeit in Haskell: Concurrent Haskell I Leichtgewichtige Threads,MVar

I Futures: Synchronisation über veränderlichen Zustand I In Haskell alsMVarmit Aktion (IO)

I In Scala alsFuture mit Callbacks

I Explizite Fehler bei Nebenläufigkeitunverzichtbar

I Morgen: Scala Collections, nächste VL: das Aktorenmodell

Referenzen

ÄHNLICHE DOKUMENTE

We address this challenge for scientific and scholarly research communities and the transformations in roles, resources, actors, and institutions of scholarship (encompassing

Related debates in academia and society have come to occupy important, ever-expanding niches (for Germany see e.g. Brand, 2014; Pennekamp, 2011), and while economic development

Naturgleichgewicht.. weist das Leitbild mit den 31000 Vollerwerbsbetrieben ausgeprägte Ge- gensätze auf, während sich im Leitbild 3 mit der vernachlässigten Land- wirtschaft

4 ème séjour Retransfert Le premier séjour est facturé par un DRG séparé, vu qu’il a été classé dans un DRG « Exception de réadmission » selon la colonne 12 du catalogue

I Reaktive Programmierung kann diese Fehlerquellen einhegen I Theoretische Grundlagen zur Modellierung nebenläufiger Systeme. I zur

I Promises sind das Gegenstück zu Futures trait Promise {. def complete(result: Try[T]) def

def execute(runnable: Runnable): Unit def reportFailure(cause: Throwable): Unit def prepare(): ExecutionContext. I Darüber kann kontrolliert werden wo der Code

I Andere funktionale Sprachen (Haskell, Erlang) haben leicht-gewichtige Threads. I Laufzeitsystem handelt Threads, Erzeugung und