-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrust-asynch
82 lines (60 loc) · 3.02 KB
/
rust-asynch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
1. Getting started
async fn foo1() {}
same as
fn foo2() -> impl Future<Output = ()> {
async {}
}
1.1 Why Async?
fn get_two_sites() {
// Spawn two threads to do work.
let thread_one = thread::spawn(|| download("https://www.foo.com"));
let thread_two = thread::spawn(|| download("https://www.bar.com"));
// Wait for both threads to complete.
thread_one.join().expect("thread one panicked");
thread_two.join().expect("thread two panicked");
}
async fn get_two_sites_async() {
// Create two different "futures" which, when run to completion,
// will asynchronously download the webpages.
let future_one = download_async("https://www.foo.com");
let future_two = download_async("https://www.bar.com");
// Run both futures to completion at the same time.
join!(future_one, future_two);
}
OS threads are suitable for a small number of tasks, since threads come with CPU and memory overhead. Spawning and switching
between threads is quite expensive as even idle threads consume system resources. A thread pool library can help mitigate some
of these costs, but not all. However, threads let you reuse existing synchronous code without significant code changes—no particular
programming model is required. In some operating systems, you can also change the priority of a thread, which is useful for drivers
and other latency sensitive applications.
Async provides significantly reduced CPU and memory overhead, especially for workloads with a large amount of IO-bound tasks, such
as servers and databases. All else equal, you can have orders of magnitude more tasks than OS threads, because an async runtime uses
a small amount of (expensive) threads to handle a large amount of (cheap) tasks. However, async Rust results in larger binary blobs
due to the state machines generated from async functions and since each executable bundles an async runtime.
1.2 The state of Asynchronous Rust
1.3 async/.await Primer
async fn learn_and_sing() {
// Wait until the song has been learned before singing it.
// We use `.await` here rather than `block_on` to prevent blocking the
// thread, which makes it possible to `dance` at the same time.
let song = learn_song().await;
sing_song(song).await;
}
async fn async_main() {
let f1 = learn_and_sing();
let f2 = dance();
// `join!` is like `.await` but can wait for multiple futures concurrently.
// If we're temporarily blocked in the `learn_and_sing` future, the `dance`
// future will take over the current thread. If `dance` becomes blocked,
// `learn_and_sing` can take back over. If both futures are blocked, then
// `async_main` is blocked and will yield to the executor.
futures::join!(f1, f2);
}
fn main() {
block_on(async_main());
}
2. Under the hood: Executing futures and tasks
2.1 The Future Trait
2.2 Task Wakeups with Waker
2.3 Applied: Build an Executor
2.4 Executors and System IO
3. async/await