Async/.await
03 декември 2019
Async/await
Async/await
- ще говорим за async/await в Rust
Async/await
- ще говорим за async/await в Rust
- нова функционалност в езика
Async/await
- ще говорим за async/await в Rust
- нова функционалност в езика
- стабилизирана в rust 1.39 (07 ноември 2019)
Async/await
- ще говорим за async/await в Rust
- нова функционалност в езика
- стабилизирана в rust 1.39 (07 ноември 2019)
- дълго чакана функционалност
Async/await
- ще говорим за async/await в Rust
- нова функционалност в езика
- стабилизирана в rust 1.39 (07 ноември 2019)
- дълго чакана функционалност
- но първо - защо async?
Защо async/await?
Проблемът
Конкурентност - искаме да изпълняваме няколко неща едновременно
fn main() -> io::Result<()> {
let contents1 = read_file("file1.txt")?;
let contents2 = read_file("file2.txt")?;
// ...
}
use std::io; fn read_file(path: &str) -> io::Result{ unimplemented!() } fn main() -> io::Result<()> { let contents1 = read_file("file1.txt")?; let contents2 = read_file("file2.txt")?; // ... Ok(()) }
Защо async/await?
Решение
Нишки
fn main() -> io::Result<()> {
let t1 = thread::spawn(|| read_file("file1.txt"));
let t2 = thread::spawn(|| read_file("file2.txt"));
let contents1 = t1.join().unwrap()?;
let contents2 = t2.join().unwrap()?;
// ...
}
use std::io; use std::thread; fn read_file(path: &str) -> io::Result{ unimplemented!() } fn main() -> io::Result<()> { let t1 = thread::spawn(|| read_file("file1.txt")); let t2 = thread::spawn(|| read_file("file2.txt")); let contents1 = t1.join().unwrap()?; let contents2 = t2.join().unwrap()?; // ... Ok(()) }
Защо async/await?
Нишки
Защо async/await?
Нишки
- удобни
Защо async/await?
Нишки
- удобни
- предоставени са ни наготово от операционната система
Защо async/await?
Нишки
- удобни
- предоставени са ни наготово от операционната система
- preemptive scheduling
Защо async/await?
Нишки
- удобни
- предоставени са ни наготово от операционната система
- preemptive scheduling
- fair scheduling
Защо async/await?
Нишки
- удобни
- предоставени са ни наготово от операционната система
- preemptive scheduling
- fair scheduling
- и т.н.
Защо async/await?
Нишки
Защо async/await?
Нишки
- недостатъци - не са подходящи ако искаме да имаме хиляди едновременни операции
Защо async/await?
Нишки
- недостатъци - не са подходящи ако искаме да имаме хиляди едновременни операции
- бавно превключване, заемат много памет, …
Защо async/await?
Нишки
- недостатъци - не са подходящи ако искаме да имаме хиляди едновременни операции
- бавно превключване, заемат много памет, …
- затова в такива ситуации се използват coroutines
Защо async/await?
Нишки
- недостатъци - не са подходящи ако искаме да имаме хиляди едновременни операции
- бавно превключване, заемат много памет, …
- затова в такива ситуации се използват coroutines
- много задачи които работят конкурентно върху малък брой нишки на ОС
Защо async/await?
Нишки
- недостатъци - не са подходящи ако искаме да имаме хиляди едновременни операции
- бавно превключване, заемат много памет, …
- затова в такива ситуации се използват coroutines
- много задачи които работят конкурентно върху малък брой нишки на ОС
- async/await е оптимизация за случаи когато се нуждаем от голямо количество нишки
Async/await в Rust
- за пример за използване на async/await ще използваме библиотеката async-std
# Cargo.toml
[dependencies]
async-std = "1.1.0"
Async/await в Rust
Std
use std::fs::File;
use std::io::{self, Read};
fn read_file(path: &str) -> io::Result<String> {
let mut file = File::open(path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
Ok(buffer)
}
use std::fs::File;
use std::io::{self, Read};
fn read_file(path: &str) -> io::Result {
let mut file = File::open(path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
Ok(buffer)
}
fn main() {}
Async/await в Rust
Async-std
use async_std::prelude::*;
use async_std::fs::File;
use async_std::io;
async fn read_file(path: &str) -> io::Result<String> {
let mut file = File::open(path).await?;
let mut buffer = String::new();
file.read_to_string(&mut buffer).await?;
Ok(buffer)
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } fn main() {}
Async/await в Rust
`async fn`
use async_std::prelude::*;
use async_std::fs::File;
use async_std::io;
// async fn декларира, че това е асинхронна функция, която връща Future
async fn read_file(path: &str) -> io::Result<String> {
let mut file = File::open(path).await?;
let mut buffer = String::new();
file.read_to_string(&mut buffer).await?;
Ok(buffer)
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; // async fn декларира, че това е асинхронна функция, която връща Future async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } fn main() {}
Async/await в Rust
Груб превод на `async fn`
async fn(...) -> T ⇒ fn(...) -> impl Future<Output = T>
use async_std::prelude::*;
use async_std::fs::File;
use async_std::io;
use std::future::Future;
fn read_file<'a>(path: &'a str) -> impl Future<Output = io::Result<String>> + 'a {
// async блок също връща `impl Future`
async move {
let mut file = File::open(path).await?;
let mut buffer = String::new();
file.read_to_string(&mut buffer).await?;
Ok(buffer)
}
}
use async_std::prelude::*; use async_std::fs::File; use async_std::io; use std::future::Future; fn read_file<'a>(path: &'a str) -> impl Future
Trait Future
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
}
pub enum Poll<T> {
Ready(T),
Pending,
}
use std::task::Context;
use std::pin::Pin;
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll;
}
pub enum Poll {
Ready(T),
Pending,
}
fn main() {}
- игнорирайте
PinиContextзасега
Trait Future
Trait Future
- декларира отложено изпълнение
Trait Future
- декларира отложено изпълнение
- наподобява closure, чието изпълнение може да бъде прекъсвано и продължавано
Trait Future
- декларира отложено изпълнение
- наподобява closure, чието изпълнение може да бъде прекъсвано и продължавано
- по само себе си future-а не прави нищо
Trait Future
- декларира отложено изпълнение
- наподобява closure, чието изпълнение може да бъде прекъсвано и продължавано
- по само себе си future-а не прави нищо
- той е мързелив
Trait Future
- декларира отложено изпълнение
- наподобява closure, чието изпълнение може да бъде прекъсвано и продължавано
- по само себе си future-а не прави нищо
- той е мързелив
- за да започне работа трябва някой да го изпълни
std::future и futures-rs
- преди да бъдат добавени към
stdfutures съществуваха в rust екосистемата като библиотеката futures
std::future и futures-rs
- преди да бъдат добавени към
stdfutures съществуваха в rust екосистемата като библиотеката futures futures 0.1е стар интерфейс, който все още се използва от някои библиотеки, има варианти за съвместимост с новия интерфейс, ако го срещнете
std::future и futures-rs
- преди да бъдат добавени към
stdfutures съществуваха в rust екосистемата като библиотеката futures futures 0.1е стар интерфейс, който все още се използва от някои библиотеки, има варианти за съвместимост с новия интерфейс, ако го срещнетеfutures 0.3е новият интерфейс, който се използва от async/await
std::future и futures-rs
- преди да бъдат добавени към
stdfutures съществуваха в rust екосистемата като библиотеката futures futures 0.1е стар интерфейс, който все още се използва от някои библиотеки, има варианти за съвместимост с новия интерфейс, ако го срещнетеfutures 0.3е новият интерфейс, който се използва от async/awaitstd::futureстабилизира основната част от интерфейса
std::future и futures-rs
- преди да бъдат добавени към
stdfutures съществуваха в rust екосистемата като библиотеката futures futures 0.1е стар интерфейс, който все още се използва от някои библиотеки, има варианти за съвместимост с новия интерфейс, ако го срещнетеfutures 0.3е новият интерфейс, който се използва от async/awaitstd::futureстабилизира основната част от интерфейсаfutures 0.3иstd::futureпредоставят едни и същи типове (trait Futureвfutures 0.3.1е reexport отstd)
std::future и futures-rs
- преди да бъдат добавени към
stdfutures съществуваха в rust екосистемата като библиотеката futures futures 0.1е стар интерфейс, който все още се използва от някои библиотеки, има варианти за съвместимост с новия интерфейс, ако го срещнетеfutures 0.3е новият интерфейс, който се използва от async/awaitstd::futureстабилизира основната част от интерфейсаfutures 0.3иstd::futureпредоставят едни и същи типове (trait Futureвfutures 0.3.1е reexport отstd)- но в библиотеката
futuresима допълнителни utilities които не са добавени вstd
Async/await в Rust
.await
- един начин да се изпълни future е накой да го
.await-не .awaitе постфиксен оператор.awaitможе да се използва само вasync fnилиasync {}
use async_std::prelude::*;
use async_std::fs::File;
use async_std::io;
async fn read_file(path: &str) -> io::Result<String> {
// File::open() -> impl Future<Output=Result<File, io::Error>>
// .await -> Result<File, io::Error>
// ? -> File
let mut file = File::open(path).await?;
let mut buffer = String::new();
file.read_to_string(&mut buffer).await?;
Ok(buffer)
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; async fn read_file(path: &str) -> io::Result{ // File::open() -> impl Future
Изпълнение
Има два начина да изпълним future
.awaitвasyncфункция или блок- да го подадем на изпълнител (executor), който ще използва
future.poll()
Задачи
- изпълнителите работят със задачи ("task"-ове)
Задачи
- изпълнителите работят със задачи ("task"-ове)
- задачата е цялото дърво от навързани future-и, които искаме да изпълним като една цяла операция
Задачи
Синхронно изпълнение
- най-простия вариант да изпълним future е да блокираме докато операцията не приключи
Задачи
Синхронно изпълнение
- най-простия вариант да изпълним future е да блокираме докато операцията не приключи
- за целта можем да използваме
task::block_onотasync_std
Задачи
Синхронно изпълнение
- най-простия вариант да изпълним future е да блокираме докато операцията не приключи
- за целта можем да използваме
task::block_onотasync_std - това създава машинарията нужна за изпълнение на задачи (припомнете си
Pin<Self>,Context)
Задачи
Синхронно изпълнение
- най-простия вариант да изпълним future е да блокираме докато операцията не приключи
- за целта можем да използваме
task::block_onотasync_std - това създава машинарията нужна за изпълнение на задачи (припомнете си
Pin<Self>,Context) - и извиква
future.pollв цикъл в текущата нишка докато future-а не върнеReady.
use async_std::task;
fn main() {
let file_contents = task::block_on(read_file("deep_quotes.txt"));
println!("{:?}", file_contents);
}
Ok("Failure is just success rounded down, my friend!\n")
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let file_contents = task::block_on(read_file("deep_quotes.txt")); println!("{:?}", file_contents); }
Задачи
Асинхронно изпълнение
- ще ни трябва някакъв executor, т.е. runtime
Задачи
Асинхронно изпълнение
- ще ни трябва някакъв executor, т.е. runtime
- има множество библиотеки които предоставят executor, но двете най-разпространени са
Задачи
Асинхронно изпълнение
- ще ни трябва някакъв executor, т.е. runtime
- има множество библиотеки които предоставят executor, но двете най-разпространени са
- tokio
Задачи
Асинхронно изпълнение
- ще ни трябва някакъв executor, т.е. runtime
- има множество библиотеки които предоставят executor, но двете най-разпространени са
- tokio
- async_std
Задачи
Асинхронно изпълнение
- от
async_stdможем да използвамеtask::spawn
Задачи
Асинхронно изпълнение
- от
async_stdможем да използвамеtask::spawn task::spawnще добави задачата към някакъв списък от задачи
Задачи
Асинхронно изпълнение
- от
async_stdможем да използвамеtask::spawn task::spawnще добави задачата към някакъв списък от задачи- които се изпълняват паралелно върху thread pool
Задачи
Асинхронно изпълнение
- от
async_stdможем да използвамеtask::spawn task::spawnще добави задачата към някакъв списък от задачи- които се изпълняват паралелно върху thread pool
- и ще върне
JoinHandle
Задачи
Асинхронно изпълнение
- от
async_stdможем да използвамеtask::spawn task::spawnще добави задачата към някакъв списък от задачи- които се изпълняват паралелно върху thread pool
- и ще върне
JoinHandle - което е future, който връща резултата от задачата
Задачи
Асинхронно изпълнение
use async_std::task;
fn main() {
let file_contents = task::block_on(async {
let deep_handle = task::spawn(read_file("deep_quotes.txt"));
let wide_handle = task::spawn(read_file("wide_quotes.txt"));
let deep = deep_handle.await;
let wide = wide_handle.await;
println!("All quotes:\n{:?}\n{:?}", deep, wide);
});
}
All quotes: Ok("Failure is just success rounded down, my friend!\n") Ok("F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !\n")
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } fn main() { let deep_contents = "Failure is just success rounded down, my friend!\n"; let wide_contents = "F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let file_contents = task::block_on(async { let deep_handle = task::spawn(read_file("deep_quotes.txt")); let wide_handle = task::spawn(read_file("wide_quotes.txt")); let deep = deep_handle.await; let wide = wide_handle.await; println!("All quotes:\n{:?}\n{:?}", deep, wide); }); }
Конкурентност и паралелизъм
Конкурентност и паралелизъм
Конкурентност - много задачи се изпълняват върху една нишка, като се редуват за резен от процесорното време
Конкурентност и паралелизъм
Конкурентност - много задачи се изпълняват върху една нишка, като се редуват за резен от процесорното време
Паралелизъм - много задачи се изпълняват по едно и също време върху много нишки
Примери
- нека направим демонстрация
Примери
- нека направим демонстрация
- за целта ще използваме функция която прочита съдържанието на файл, но бавно
use async_std::task;
use std::time::Duration;
async fn fetch_file(path: &str) -> io::Result<String> {
task::sleep(Duration::from_secs(1)).await;
read_file(path).await
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; use std::time::Duration; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(Duration::from_secs(1)).await; read_file(path).await } fn main() {}
Примери
- нека направим демонстрация
- за целта ще използваме функция която прочита съдържанието на файл, но бавно
use async_std::task;
use std::time::Duration;
async fn fetch_file(path: &str) -> io::Result<String> {
task::sleep(Duration::from_secs(1)).await;
read_file(path).await
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; use std::time::Duration; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(Duration::from_secs(1)).await; read_file(path).await } fn main() {}
- забележете, че използваме
async_std::task::sleepвместоstd::thread::sleep
Примери
- нека направим демонстрация
- за целта ще използваме функция която прочита съдържанието на файл, но бавно
use async_std::task;
use std::time::Duration;
async fn fetch_file(path: &str) -> io::Result<String> {
task::sleep(Duration::from_secs(1)).await;
read_file(path).await
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; use std::time::Duration; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(Duration::from_secs(1)).await; read_file(path).await } fn main() {}
- забележете, че използваме
async_std::task::sleepвместоstd::thread::sleep async_stdпредоставя алтернативи на стандартните типове и функции, които блокират текущата задача, а не текущата нишка
Примери
- нека направим демонстрация
- за целта ще използваме функция която прочита съдържанието на файл, но бавно
use async_std::task;
use std::time::Duration;
async fn fetch_file(path: &str) -> io::Result<String> {
task::sleep(Duration::from_secs(1)).await;
read_file(path).await
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; use std::time::Duration; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(Duration::from_secs(1)).await; read_file(path).await } fn main() {}
- забележете, че използваме
async_std::task::sleepвместоstd::thread::sleep async_stdпредоставя алтернативи на стандартните типове и функции, които блокират текущата задача, а не текущата нишка- други важни типове - мутекс, канали, …
Примери
- нека направим демонстрация
- за целта ще използваме функция която прочита съдържанието на файл, но бавно
use async_std::task;
use std::time::Duration;
async fn fetch_file(path: &str) -> io::Result<String> {
task::sleep(Duration::from_secs(1)).await;
read_file(path).await
}
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; use std::time::Duration; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(Duration::from_secs(1)).await; read_file(path).await } fn main() {}
- забележете, че използваме
async_std::task::sleepвместоstd::thread::sleep async_stdпредоставя алтернативи на стандартните типове и функции, които блокират текущата задача, а не текущата нишка- други важни типове - мутекс, канали, …
- важно е да не използваме блокиращи функции в асинхронен код, защото като блокираме изпълняващата нишка никоя друга задача не може да се изпълни на нея
Примери
let begin = std::time::Instant::now();
task::block_on(async {
let deep = fetch_file("deep_quotes.txt").await;
let wide = fetch_file("wide_quotes.txt").await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 2.002997323s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep = fetch_file("deep_quotes.txt").await; let wide = fetch_file("wide_quotes.txt").await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
Примери
let begin = std::time::Instant::now();
task::block_on(async {
let deep = fetch_file("deep_quotes.txt").await;
let wide = fetch_file("wide_quotes.txt").await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 2.002997323s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep = fetch_file("deep_quotes.txt").await; let wide = fetch_file("wide_quotes.txt").await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- тази имплементация е синхронна - изчакваме първият файл да се прочете преди да започнем да четем втория
Примери
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let deep = deep_future.await;
let wide = wide_future.await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 2.00271393s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let deep = deep_future.await; let wide = wide_future.await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
Примери
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let deep = deep_future.await;
let wide = wide_future.await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 2.00271393s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let deep = deep_future.await; let wide = wide_future.await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- отново синхронна - future-ите не правят нищо докато не им извикаме
pollили.await
Примери - конкурентност
use futures::join;
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let (deep, wide) = join!(deep_future, wide_future);
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.001517179s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); use futures::join; let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let (deep, wide) = join!(deep_future, wide_future); println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
Примери - конкурентност
use futures::join;
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let (deep, wide) = join!(deep_future, wide_future);
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.001517179s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); use futures::join; let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let (deep, wide) = join!(deep_future, wide_future); println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- това е пример за конкурентно изпълнение
Примери - конкурентност
use futures::join;
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let (deep, wide) = join!(deep_future, wide_future);
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.001517179s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); use futures::join; let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let (deep, wide) = join!(deep_future, wide_future); println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- това е пример за конкурентно изпълнение
- всичко се изпълнява само на главната нишка
Примери - конкурентност
use futures::join;
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let (deep, wide) = join!(deep_future, wide_future);
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.001517179s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); use futures::join; let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let (deep, wide) = join!(deep_future, wide_future); println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- това е пример за конкурентно изпълнение
- всичко се изпълнява само на главната нишка
- използваме join! от библиотеката
futures
Примери - конкурентност
use futures::join;
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let (deep, wide) = join!(deep_future, wide_future);
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.001517179s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); use futures::join; let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let (deep, wide) = join!(deep_future, wide_future); println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- това е пример за конкурентно изпълнение
- всичко се изпълнява само на главната нишка
- използваме join! от библиотеката
futures join!се опитва да позволи на всеки future да прогресира
Примери - конкурентност
use futures::join;
let begin = std::time::Instant::now();
task::block_on(async {
let deep_future = fetch_file("deep_quotes.txt");
let wide_future = fetch_file("wide_quotes.txt");
let (deep, wide) = join!(deep_future, wide_future);
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.001517179s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); use futures::join; let begin = std::time::Instant::now(); task::block_on(async { let deep_future = fetch_file("deep_quotes.txt"); let wide_future = fetch_file("wide_quotes.txt"); let (deep, wide) = join!(deep_future, wide_future); println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- това е пример за конкурентно изпълнение
- всичко се изпълнява само на главната нишка
- използваме join! от библиотеката
futures join!се опитва да позволи на всеки future да прогресира- извиква
pollвърху първия, ако той върне че ще блокира извикваpollвърху втория, и т.н.
Примери - паралелизъм
let begin = std::time::Instant::now();
task::block_on(async {
let deep = task::spawn(fetch_file("deep_quotes.txt")).await;
let wide = task::spawn(fetch_file("wide_quotes.txt")).await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 2.003720327s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep = task::spawn(fetch_file("deep_quotes.txt")).await; let wide = task::spawn(fetch_file("wide_quotes.txt")).await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
Примери - паралелизъм
let begin = std::time::Instant::now();
task::block_on(async {
let deep = task::spawn(fetch_file("deep_quotes.txt")).await;
let wide = task::spawn(fetch_file("wide_quotes.txt")).await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 2.003720327s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep = task::spawn(fetch_file("deep_quotes.txt")).await; let wide = task::spawn(fetch_file("wide_quotes.txt")).await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- отново синхронно - чакаме да прочетем първия файл преди да пуснем втория таск
Примери - паралелизъм
let begin = std::time::Instant::now();
task::block_on(async {
let deep_handle = task::spawn(fetch_file("deep_quotes.txt"));
let wide_handle = task::spawn(fetch_file("wide_quotes.txt"));
let deep = deep_handle.await;
let wide = wide_handle.await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.002705287s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep_handle = task::spawn(fetch_file("deep_quotes.txt")); let wide_handle = task::spawn(fetch_file("wide_quotes.txt")); let deep = deep_handle.await; let wide = wide_handle.await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
Примери - паралелизъм
let begin = std::time::Instant::now();
task::block_on(async {
let deep_handle = task::spawn(fetch_file("deep_quotes.txt"));
let wide_handle = task::spawn(fetch_file("wide_quotes.txt"));
let deep = deep_handle.await;
let wide = wide_handle.await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.002705287s
extern crate async_std; use async_std::prelude::*; use async_std::fs::File; use async_std::io; use async_std::task; async fn read_file(path: &str) -> io::Result{ let mut file = File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep_handle = task::spawn(fetch_file("deep_quotes.txt")); let wide_handle = task::spawn(fetch_file("wide_quotes.txt")); let deep = deep_handle.await; let wide = wide_handle.await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- паралелно - задачите започват да се изпълняват в момента в който извикаме
task::spawn
Справяне с блокиращи операции
fn fetch_file_sync(path: &str) -> io::Result<String> {
thread::sleep(Duration::from_millis(1500));
read_file_sync(path)
}
use std::fs::File;
use std::io::{self, Read};
use std::thread;
use std::time::Duration;
fn read_file_sync(path: &str) -> io::Result {
let mut file = File::open(path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
Ok(buffer)
}
fn fetch_file_sync(path: &str) -> io::Result {
thread::sleep(Duration::from_millis(1500));
read_file_sync(path)
}
fn main() {}
Справяне с блокиращи операции
let begin = std::time::Instant::now();
task::block_on(async {
let deep_handle = task::spawn(fetch_file("deep_quotes.txt"));
let wide_handle = task::spawn_blocking(|| fetch_file_sync("wide_quotes.txt"));
let deep = deep_handle.await;
let wide = wide_handle.await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.500905977s
extern crate async_std; use async_std::prelude::*; use async_std::task; use std::io::Read as _; async fn read_file(path: &str) -> async_std::io::Result{ let mut file = async_std::fs::File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> async_std::io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn read_file_sync(path: &str) -> std::io::Result { let mut file = std::fs::File::open(path)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; Ok(buffer) } fn fetch_file_sync(path: &str) -> std::io::Result { std::thread::sleep(std::time::Duration::from_millis(1500)); read_file_sync(path) } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep_handle = task::spawn(fetch_file("deep_quotes.txt")); let wide_handle = task::spawn_blocking(|| fetch_file_sync("wide_quotes.txt")); let deep = deep_handle.await; let wide = wide_handle.await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
Справяне с блокиращи операции
let begin = std::time::Instant::now();
task::block_on(async {
let deep_handle = task::spawn(fetch_file("deep_quotes.txt"));
let wide_handle = task::spawn_blocking(|| fetch_file_sync("wide_quotes.txt"));
let deep = deep_handle.await;
let wide = wide_handle.await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Ok("W i d e!\n") Работата отне: 1.500905977s
extern crate async_std; use async_std::prelude::*; use async_std::task; use std::io::Read as _; async fn read_file(path: &str) -> async_std::io::Result{ let mut file = async_std::fs::File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> async_std::io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn read_file_sync(path: &str) -> std::io::Result { let mut file = std::fs::File::open(path)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; Ok(buffer) } fn fetch_file_sync(path: &str) -> std::io::Result { std::thread::sleep(std::time::Duration::from_millis(1500)); read_file_sync(path) } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep_handle = task::spawn(fetch_file("deep_quotes.txt")); let wide_handle = task::spawn_blocking(|| fetch_file_sync("wide_quotes.txt")); let deep = deep_handle.await; let wide = wide_handle.await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
- task::spawn_blocking - използва отделен threadpool само за блокиращи операции, за да не се блокират нишките които изпълняват задачи
Справяне с блокиращи операции
Вариант за ръчна имплементация, ако нямаше task::spawn_blocking
let begin = std::time::Instant::now();
task::block_on(async {
let deep_handle = task::spawn(fetch_file("deep_quotes.txt"));
let wide_handle = task::spawn(async {
let (sender, receiver) = async_std::sync::channel(1);
std::thread::spawn(move || {
let res = fetch_file_sync("wide_quotes.txt");
task::block_on(sender.send(res));
});
receiver.recv().await
});
let deep = deep_handle.await;
let wide = wide_handle.await;
println!("All quotes: {:?} {:?}", deep, wide);
});
println!("Работата отне: {:?}", begin.elapsed());
All quotes: Ok("Deep!\n") Some(Ok("W i d e!\n")) Работата отне: 1.501117439s
extern crate async_std; use async_std::prelude::*; use async_std::task; use std::io::Read as _; async fn read_file(path: &str) -> async_std::io::Result{ let mut file = async_std::fs::File::open(path).await?; let mut buffer = String::new(); file.read_to_string(&mut buffer).await?; Ok(buffer) } async fn fetch_file(path: &str) -> async_std::io::Result { task::sleep(std::time::Duration::from_secs(1)).await; read_file(path).await } fn read_file_sync(path: &str) -> std::io::Result { let mut file = std::fs::File::open(path)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; Ok(buffer) } fn fetch_file_sync(path: &str) -> std::io::Result { std::thread::sleep(std::time::Duration::from_millis(1500)); read_file_sync(path) } fn main() { let deep_contents = "Deep!\n"; let wide_contents = "W i d e!\n"; std::fs::write("deep_quotes.txt", deep_contents.as_bytes()).unwrap(); std::fs::write("wide_quotes.txt", wide_contents.as_bytes()).unwrap(); let begin = std::time::Instant::now(); task::block_on(async { let deep_handle = task::spawn(fetch_file("deep_quotes.txt")); let wide_handle = task::spawn(async { let (sender, receiver) = async_std::sync::channel(1); std::thread::spawn(move || { let res = fetch_file_sync("wide_quotes.txt"); task::block_on(sender.send(res)); }); receiver.recv().await }); let deep = deep_handle.await; let wide = wide_handle.await; println!("All quotes: {:?} {:?}", deep, wide); }); println!("Работата отне: {:?}", begin.elapsed()); }
Имплементация на futures
Стандартен стек
- трябва да ви е познато от курса по C++
- но ако не ви е:
- парче памет, което е заделено за вашата нишка да си пише неща
- използва се при викане на функции
- аргументи, локални променливи, временни стойности, резултати, регистри, …
- използва се като стек - последният влязъл елемент е първият излязъл
Стандартен стек
- извикване на функция
- добавя се нова стекова рамка на върха на стека
Стандартен стек
- връщане от функция
- премахва се стековата рамка от върха на стека
Stackfull coroutine
- корутини които наподобяват нишки
Stackfull coroutine
- корутини които наподобяват нишки
- всяка нишка си има стек
Stackfull coroutine
- корутини които наподобяват нишки
- всяка нишка си има стек
- имитират изпълнението на нишки от операционната система
Stackfull coroutine
- корутини които наподобяват нишки
- всяка нишка си има стек
- имитират изпълнението на нишки от операционната система
- повечето езици които поддържат promises, futures или async/await използват тази имплементация
Stackfull coroutine
- корутини които наподобяват нишки
- всяка нишка си има стек
- имитират изпълнението на нишки от операционната система
- повечето езици които поддържат promises, futures или async/await използват тази имплементация
- наричат се още - зелени нишки, fibers, корутини, …
Stackfull coroutine
Как работят
Stackfull coroutine
Как работят
- нека имаме стандартна нишка на ОС, която изпълнява функция
foo1
Stackfull coroutine
Как работят
- нека имаме стандартна нишка на ОС, която изпълнява функция
foo1 foo1е извикалаfoo2
Stackfull coroutine
foo2иска да изпълни асинхронна функция
Stackfull coroutine
foo2иска да изпълни асинхронна функция- алокира се нов стек за
BAR
Stackfull coroutine
foo2иска да изпълни асинхронна функция- алокира се нов стек за
BAR - променя се адреса на стека (процесорът държи указател към върха на стека)
Stackfull coroutine
foo2иска да изпълни асинхронна функция- алокира се нов стек за
BAR - променя се адреса на стека (процесорът държи указател към върха на стека)
- извиква се функцията
bar1в новия стек
Stackfull coroutine
foo2иска да изпълни асинхронна функция- алокира се нов стек за
BAR - променя се адреса на стека (процесорът държи указател към върха на стека)
- извиква се функцията
bar1в новия стек - процесора продължава да си работи все едно нищо не се е променило
Stackfull coroutine
bar1извикваbar2bar2извикваbar3
Stackfull coroutine
- функцията
bar3е асинхронна и иска да прекъсне
Stackfull coroutine
- функцията
bar3е асинхронна и иска да прекъсне - (например чака за входно-изходна операция)
Stackfull coroutine
- функцията
bar3е асинхронна и иска да прекъсне - (например чака за входно-изходна операция)
- за целта пренасочва адреса на стека обратно към оригиналната памет
Stackfull coroutine
- функцията
bar3е асинхронна и иска да прекъсне - (например чака за входно-изходна операция)
- за целта пренасочва адреса на стека обратно към оригиналната памет
- и се "връща" в
foo2
Stackfull coroutine
- оттам нататък
foo2може да извика друга корутина
Stackfull coroutine
- оттам нататък
foo2може да извика друга корутина - или да продължи изпълнението на
bar3
Stackfull coroutine
Предимства
- прекъсване по всяко време
- корутината си пази състоянието в което е прекъсната - всичко е на стека
- може да се продължи от същото състояние
Недостатъци
- заема се много памет за стекове
- преоразмеряването на стекове е проблемно
Stackless coroutines
- корутини без стек
Stackless coroutines
- корутини без стек
- представляват една машина на състояния
Stackless coroutines
- корутини без стек
- представляват една машина на състояния
- прогреса представлява преминаване от едно състояние в следващо
Stackless coroutines
- корутини без стек
- представляват една машина на състояния
- прогреса представлява преминаване от едно състояние в следващо
- по-рядко се използват - виждал съм ги само на две места
Stackless coroutines
- корутини без стек
- представляват една машина на състояния
- прогреса представлява преминаване от едно състояние в следващо
- по-рядко се използват - виждал съм ги само на две места
- futures в rust
- предложението за корутини в c++20
Stackless coroutines
Как работят
Stackless coroutines
Как работят
- отново започваме с нишка на ОС
Stackless coroutines
Как работят
- отново започваме с нишка на ОС
- нишката изпълнява функциите
foo1иfoo2
Stackless coroutines
foo2започва да изпълнява корутинаBAR
Stackless coroutines
foo2започва да изпълнява корутинаBAR- заделя машината от състояния на
BAR(напр.Box<BarState>)
Stackless coroutines
foo2започва да изпълнява корутинаBAR- заделя машината от състояния на
BAR(напр.Box<BarState>) - и извиква
bar1като съвсем нормална функция (напр.bar1(&mut bar_state))
Stackless coroutines
foo2започва да изпълнява корутинаBAR- заделя машината от състояния на
BAR(напр.Box<BarState>) - и извиква
bar1като съвсем нормална функция (напр.bar1(&mut bar_state)) bar1извиква други функции
Stackless coroutines
- функцията
bar3иска да прекъсне
Stackless coroutines
- функцията
bar3иска да прекъсне - но за да се върне до
foo2трябва да освободи стека
Stackless coroutines
- функцията
bar3иска да прекъсне - но за да се върне до
foo2трябва да освободи стека - запазва текущото си състояние
Stackless coroutines
- функцията
bar3иска да прекъсне - но за да се върне до
foo2трябва да освободи стека - запазва текущото си състояние
- връща
NotReady
Stackless coroutines
- понеже
bar3е върнала, че не може да продължи, значиbar2също не може
Stackless coroutines
- понеже
bar3е върнала, че не може да продължи, значиbar2също не може bar2си запазва състоянието и връщаNotReady
Stackless coroutines
- понеже
bar3е върнала, че не може да продължи, значиbar2също не може bar2си запазва състоянието и връщаNotReadybar1си запазва състоянието и връщаNotReady
Stackless coroutines
- ако искаме да продължим изпълнението на
bar3трябва отново:
Stackless coroutines
- ако искаме да продължим изпълнението на
bar3трябва отново: foo2да извикаbar1
Stackless coroutines
- ако искаме да продължим изпълнението на
bar3трябва отново: foo2да извикаbar1bar1да извикаbar2
Stackless coroutines
- ако искаме да продължим изпълнението на
bar3трябва отново: foo2да извикаbar1bar1да извикаbar2bar2да извикаbar3
Stackless coroutines
Предимства
- евтини за създаване - трябва да се задели само структурата пазеща state машината
- използват стека на нишката в която се изпълняват (добре от гледна точка на процесорен кеш)
Недостатъци
- трябва езикът/компилаторът да може да генерира state машината
- по-сложно за имплементиране от stackfull coroutines
Разгъване на .await
let mut file = File::open(path).await?;
let mut buffer = String::new();
file.read_to_string(&mut buffer).await?;
Ok(buffer)
Разгъване на .await
fn read_file(path: &str) -> ReadFileFut {
ReadFileFut::Initialized { path }
}
enum ReadFileFut<'a> {
Initialized { path: &'a str },
FileOpen { fut: FileOpenFut },
ReadToString { file: File, buffer: String, fut: ReadToStringFut },
Done,
}
use async_std::fs::File;
fn read_file(path: &str) -> ReadFileFut {
ReadFileFut::Initialized { path }
}
enum ReadFileFut<'a> {
Initialized { path: &'a str },
FileOpen { fut: FileOpenFut },
ReadToString { file: File, buffer: String, fut: ReadToStringFut },
Done,
}
struct FileOpenFut;
struct ReadToStringFut;
fn main() {}
Разгъване на .await
impl<'a> Future for ReadFileFut<'a> {
type Output = io::Result<String>;
fn poll(self: Pin<&mut Self>, _ctx: Context) -> Poll<Self::Output> {
loop {
match self {
ReadFileFut::Initialized { .. } => { /* ... */ },
ReadFileFut::FileOpen { .. } => { /* ... */ },
ReadFileFut::ReadToString { .. } => { /* ... */ },
ReadFileFut::Done => { /* ... */ },
}
}
}
}
Разгъване на .await
Initialized { path }
Разгъване на .await
Initialized { path }
let fut = File::open(path)self = FileOpen { fut }
Разгъване на .await
FileOpen { fut }
Разгъване на .await
FileOpen { fut }
- извикваме
fut.poll()Poll::NotReady=>return Poll::NotReadyPoll::Ready(file)=>file?Err(e)self = Donereturn Poll::Ready(Err(e.into()))
Ok(file)let buffer = String::new()let fut = file.read_to_string(&mut buffer)self = ReadToString { file, buffer, fut }
Разгъване на .await
ReadToString { buffer, fut, .. }
Разгъване на .await
ReadToString { buffer, fut, .. }
- извикваме
fut.poll()Poll::NotReady=>return Poll::NotReadyPoll::Ready(res)=>res?Err(e)self = Donereturn Poll::Ready(Err(e.into()))
Okself = Donereturn Poll::Ready(Ok(buffer))
Разгъване на .await
ReadFileFut::Done
Разгъване на .await
ReadFileFut::Done
panic!()
Обобщение
Обобщение
- за всеки
async fnилиasync {}се генерира state машина
Обобщение
- за всеки
async fnилиasync {}се генерира state машина - машината се репрезентира като енумерация
Обобщение
- за всеки
async fnилиasync {}се генерира state машина - машината се репрезентира като енумерация
- за вариант за всяка ситуация в която се
.await-ва future
Обобщение
- за всеки
async fnилиasync {}се генерира state машина - машината се репрезентира като енумерация
- за вариант за всяка ситуация в която се
.await-ва future - когато машината изчаква future и ѝ извикаме
poll
Обобщение
- за всеки
async fnилиasync {}се генерира state машина - машината се репрезентира като енумерация
- за вариант за всяка ситуация в която се
.await-ва future - когато машината изчаква future и ѝ извикаме
poll- ще извика
pollна вътрешния future
- ще извика
Обобщение
- за всеки
async fnилиasync {}се генерира state машина - машината се репрезентира като енумерация
- за вариант за всяка ситуация в която се
.await-ва future - когато машината изчаква future и ѝ извикаме
poll- ще извика
pollна вътрешния future - ако той върне
NotReady- връщаNotReady
- ще извика
Обобщение
- за всеки
async fnилиasync {}се генерира state машина - машината се репрезентира като енумерация
- за вариант за всяка ситуация в която се
.await-ва future - когато машината изчаква future и ѝ извикаме
poll- ще извика
pollна вътрешния future - ако той върне
NotReady- връщаNotReady - ако той върне
Ready- продължава към следващото състояние
- ще извика
Допълнителна информация
- https://www.youtube.com/watch?v=L7X0vpAU-sU - презентация за async-std
- https://book.async.rs/ - книгата за async std, туториала в който имплементират чат сървър е много добър