Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HTWG: Events, Send & Sync

HTWG: Events, Send & Sync

Gastvorlesung an der HTWG Konstanz zu Rust Futures

Florian Gilcher

May 23, 2017
Tweet

More Decks by Florian Gilcher

Other Decks in Technology

Transcript

  1. Option 1: Threads r Berechnungen werden nebenläufig ausgeführt r Ein

    Scheduler verwaltet die Ausführung r Haben einen eigenen Stack r Teilen den Heap r Können einzeln blockieren
  2. Vorteile r Folgen im Einzelnen einem (relativ) linearen Modell r

    Auf (fast) allen Betriebssystem nativ unterstützt r Threads haben einen eigenen Spe- icherbereich (thread-local storage) r Stacks können zum debuggen be- nutzt werden
  3. Nachteile r Jeder Thread braucht Platz für einen Stack r

    Es können recht einfach Synchro- nisierungsbugs auftreten
  4. r Jede Aktion ist ein "Event" r Das System hält

    eine Liste zu bear- beitender Events r Eine große Schleife arbeitet diese Events nach und nach ab r Events können weitere Events auslösen r Aktionen haben keine Verbindung über den Stack
  5. Vorteile r Eine Aktion zu einer Zeit: weniger Synchronisierungsprobleme r

    Blockaden werden durch Einreihen ans Ende der Queue abgebildet r Blockaden sind sehr günstig
  6. Nachteile r Aktionen, die den ausführenden Thread doch blockieren, blockieren

    alles r Schwer zu folgen, da Reihenfolge selbst herzustellen ist
  7. Send Ein Wert kann zwischen Threads abgegeben werden. Der sendende

    Thread verliert den Zugriff. Der Zugriff bleibt exklusiv.
  8. Sync Ein Wert kann zwischen Threads geteilt werden. Der sendende

    Thread behält den Zugriff. Der Zugriff wird geteilt.
  9. Send und Sync sind elegant Sie sagen nichts spezifisches über

    die verwendete Technologie aus. Sie erlauben aber spezifisches Arbeiten mit einer Technologie.
  10. Futures trait Future { type Item; type Error; fn poll(&mut

    self) -> Result<Async<T>, E>; fn wait(self) -> Result<T, E>; }
  11. Async r Ok(Async::Ready(t)) -> Die Future ist fertig r Ok(Async::NotReady)

    -> Die Future arbeitet noch r Err(e) -> Ein Fehler ist aufgetreten
  12. Futures Futures abstrahieren eine Berechnung, die in der Zukunft beendet

    sein könnte und entweder erfolgreich oder zu einem Fehler führen wird.
  13. Lingo r Eine Future ist "aufgelöst", wenn das Ergebnis da

    ist. r "deferred computation" r Futures am Ende einer Berechnung heissen "Blätter"
  14. fn main() { let timer = Timer::default(); let sleep =

    timer.sleep(Duration::from_millis(1000)) .inspect("sleep"); let cpu_pool = CpuPool::new(4); let task = cpu_pool.spawn(sleep); println!("{:?}", task.wait()); }
  15. tokio-core tokio-core hält eine Liste ausführbarer Futures und arbeitet diese

    eine nach dem anderen ab. Futures, die gerade nicht ausführbar sind, werden zurück in die Liste gebracht.
  16. use tokio_core::reactor::Core; fn main() { let timer = Timer::default(); let

    sleep = timer.sleep(Duration::from_millis(1000)) .inspect("sleep"); let mut core = Core::new().unwrap(); let task = core.run(sleep); println!("{:?}", task); }
  17. Protokoll r Futures werden beim ersten poll() gestartet r Sie

    können beliebig oft "NotReady" antworten r Ein weiterer Aufruf von poll() nach "Ready" ist nicht erlaubt
  18. Kombination von Futures fn main() { let timer = Timer::default();

    let sleep = timer.sleep(Duration::from_millis(1000)) .inspect("sleep") .and_then(|_| { timer.sleep(Duration::from_millis(500)) .inspect("sleep some more") }); let mut core = Core::new().unwrap(); let task = core.run(sleep); println!("{:?}", task); }
  19. Future sleep polled: Ok(NotReady) Future sleep polled: Ok(Ready(())) Future sleep

    some more polled: Ok(NotReady) Future sleep some more polled: Ok(Ready(())) Ok(())
  20. Kombinatoren: join_all join_all fügt mehrere Futures zu einer neuen Future

    zusammen, die aufgelöst wird, wenn alle Blätter aufgelöst werden.
  21. fn main() { let timer = Timer::default(); let sleep =

    timer.sleep(Duration::from_millis(1500)) .inspect("sleep"); let sleep_shorter = timer.sleep(Duration::from_millis(500)) .inspect("short sleep"); let join = join_all(vec![sleep, sleep_shorter]) .inspect("join"); let mut core = Core::new().unwrap(); let result = core.run(join); println!("{:?}", result); }
  22. Future sleep polled: Ok(NotReady) Future short sleep polled: Ok(NotReady) Future

    join polled: Ok(NotReady) Future sleep polled: Ok(NotReady) Future short sleep polled: Ok(Ready(())) Future join polled: Ok(NotReady) Future sleep polled: Ok(Ready(())) Future join polled: Ok(Ready([(), ()])) Ok([(), ()])
  23. Kombinatoren: select_all select_all fügt mehrere Futures zu einer neuen Future

    zusammen, die aufgelöst wird, wenn das erste Kind aufgelöst werden.
  24. fn main() { let timer = Timer::default(); let sleep =

    timer.sleep(Duration::from_millis(1000)) .inspect("sleep"); let short_sleep = timer.sleep(Duration::from_millis(500)) .inspect("short sleep");
  25. let select = select_all(vec![sleep, short_sleep]) .inspect("first select"); let mut core

    = Core::new().unwrap(); let (result, index, remaining_futures) = core.run(select).un println!("Future with index {} returned {:?}", index, result); let select = select_all(remaining_futures) .inspect("second select"); let (result, index, _) = core.run(select).unwrap(); println!("Future with index {} returned {:?}", index, result); }
  26. Future sleep polled: Ok(NotReady) Future short sleep polled: Ok(NotReady) Future

    first select polled: Ok(NotReady) Future sleep polled: Ok(NotReady) Future short sleep polled: Ok(Ready(())) Future first select polled: Ok(Ready(((), 1, [ InspectFuture { future: Sleep { timer: Timer Future with index 1 returned () Future sleep polled: Ok(NotReady) Future second select polled: Ok(NotReady) Future sleep polled: Ok(Ready(())) Future second select polled: Ok(Ready(((), 0, [] Future with index 0 returned ()
  27. Eine Umformung let select = select_all(vec![sleep, short_sleep]) .inspect("select_all") .and_then(|(result, index,

    futures)| { println!("Future with index {} returned {:?}", index, result); select_all(futures) .inspect("nested select_all") }); let mut core = Core::new().unwrap(); let (result, index, _) = core.run(select).unwrap(); println!("Future with index {} returned {:?}", index, result);
  28. Future sleep polled: Ok(NotReady) Future short sleep polled: Ok(NotReady) Future

    select_all polled: Ok(NotReady) Future sleep polled: Ok(NotReady) Future short sleep polled: Ok(Ready(())) Future select_all polled: Ok(Ready(((), 1, [ InspectFuture { future: Sleep { timer: Timer Future with index 1 returned () Future sleep polled: Ok(NotReady) Future nested select_all polled: Ok(NotReady) Future sleep polled: Ok(Ready(())) Future nested select_all polled: Ok(Ready(((), 0 Future with index 0 returned ()
  29. Tokio Futures werden selten so roh wie hier verwendet. Als

    Beispiel kann das Tokio-Framework dienen. https://tokio.rs/
  30. Halb-synchron: Tokio-Service pub trait Service { /// Requests handled by

    the service. type Request; /// Responses given by the service. type Response; /// Errors produced by the service. type Error; /// The future response value. type Future: Future<Item = Self::Response, Error = Self::Error>; /// Process the request and return the response asynchronous fn call(&self, req: Self::Request) -> Self::Future; }
  31. r Nimmt synchron eine Anfrage an r Gibt einen Wert

    zurück, der eine zukünftige Antwort anzeigt r Das asynchrone Aufrufen des Ser- vices übernimmt Tokio
  32. impl Service for MailboxService<String> { type Request = redisish::Command; type

    Response = ServerResponse; type Error = io::Error; type Future = FutureResult<Self::Response, Self::Error>; fn call(&self, command: Self::Request) -> Self::Future { /// .... } }