You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-12-03 09:45:17 +02:00
Update Concurrency course with times (#2007)
As I mentioned in #1536: * Break into segments at approximately the places @fw-immunant put breaks * Move all of the files into `src/concurrency` * Add timings and segment/session metadata so course outlines appear There's room for more work here, including some additional feedback from @fw-immunant after the session I observed, but let's do one step at a time :)
This commit is contained in:
committed by
GitHub
parent
a03b7e68e5
commit
face5af783
@@ -353,46 +353,50 @@
|
||||
|
||||
---
|
||||
|
||||
- [Welcome](concurrency.md)
|
||||
- [Welcome](concurrency/welcome.md)
|
||||
- [Threads](concurrency/threads.md)
|
||||
- [Scoped Threads](concurrency/scoped-threads.md)
|
||||
- [Plain Threads](concurrency/threads/plain.md)
|
||||
- [Scoped Threads](concurrency/threads/scoped.md)
|
||||
- [Channels](concurrency/channels.md)
|
||||
- [Senders and Reveivers](concurrency/channels/senders-receivers.md)
|
||||
- [Unbounded Channels](concurrency/channels/unbounded.md)
|
||||
- [Bounded Channels](concurrency/channels/bounded.md)
|
||||
- [`Send` and `Sync`](concurrency/send-sync.md)
|
||||
- [Marker Traits](concurrency/send-sync/marker-traits.md)
|
||||
- [`Send`](concurrency/send-sync/send.md)
|
||||
- [`Sync`](concurrency/send-sync/sync.md)
|
||||
- [Examples](concurrency/send-sync/examples.md)
|
||||
- [Shared State](concurrency/shared_state.md)
|
||||
- [`Arc`](concurrency/shared_state/arc.md)
|
||||
- [`Mutex`](concurrency/shared_state/mutex.md)
|
||||
- [Example](concurrency/shared_state/example.md)
|
||||
- [Exercises](exercises/concurrency/morning.md)
|
||||
- [Dining Philosophers](exercises/concurrency/dining-philosophers.md)
|
||||
- [Multi-threaded Link Checker](exercises/concurrency/link-checker.md)
|
||||
- [Solutions](exercises/concurrency/solutions-morning.md)
|
||||
- [Shared State](concurrency/shared-state.md)
|
||||
- [`Arc`](concurrency/shared-state/arc.md)
|
||||
- [`Mutex`](concurrency/shared-state/mutex.md)
|
||||
- [Example](concurrency/shared-state/example.md)
|
||||
- [Exercises](concurrency/sync-exercises.md)
|
||||
- [Dining Philosophers](concurrency/sync-exercises/dining-philosophers.md)
|
||||
- [Multi-threaded Link Checker](concurrency/sync-exercises/link-checker.md)
|
||||
- [Solutions](concurrency/sync-exercises/solutions.md)
|
||||
|
||||
# Concurrency: Afternoon
|
||||
|
||||
- [Async Basics](async.md)
|
||||
- [`async`/`await`](async/async-await.md)
|
||||
- [Futures](async/futures.md)
|
||||
- [Runtimes](async/runtimes.md)
|
||||
- [Tokio](async/runtimes/tokio.md)
|
||||
- [Tasks](async/tasks.md)
|
||||
- [Async Channels](async/channels.md)
|
||||
- [Control Flow](async/control-flow.md)
|
||||
- [Join](async/control-flow/join.md)
|
||||
- [Select](async/control-flow/select.md)
|
||||
- [Pitfalls](async/pitfalls.md)
|
||||
- [Blocking the Executor](async/pitfalls/blocking-executor.md)
|
||||
- [`Pin`](async/pitfalls/pin.md)
|
||||
- [Async Traits](async/pitfalls/async-traits.md)
|
||||
- [Cancellation](async/pitfalls/cancellation.md)
|
||||
- [Exercises](exercises/concurrency/afternoon.md)
|
||||
- [Dining Philosophers](exercises/concurrency/dining-philosophers-async.md)
|
||||
- [Broadcast Chat Application](exercises/concurrency/chat-app.md)
|
||||
- [Solutions](exercises/concurrency/solutions-afternoon.md)
|
||||
- [Welcome](concurrency/welcome-async.md)
|
||||
- [Async Basics](concurrency/async.md)
|
||||
- [`async`/`await`](concurrency/async/async-await.md)
|
||||
- [Futures](concurrency/async/futures.md)
|
||||
- [Runtimes](concurrency/async/runtimes.md)
|
||||
- [Tokio](concurrency/async/runtimes/tokio.md)
|
||||
- [Tasks](concurrency/async/tasks.md)
|
||||
- [Channels and Control Flow](concurrency/async-control-flow.md)
|
||||
- [Async Channels](concurrency/async-control-flow/channels.md)
|
||||
- [Join](concurrency/async-control-flow/join.md)
|
||||
- [Select](concurrency/async-control-flow/select.md)
|
||||
- [Pitfalls](concurrency/async-pitfalls.md)
|
||||
- [Blocking the Executor](concurrency/async-pitfalls/blocking-executor.md)
|
||||
- [`Pin`](concurrency/async-pitfalls/pin.md)
|
||||
- [Async Traits](concurrency/async-pitfalls/async-traits.md)
|
||||
- [Cancellation](concurrency/async-pitfalls/cancellation.md)
|
||||
- [Exercises](concurrency/async-exercises.md)
|
||||
- [Dining Philosophers](concurrency/async-exercises/dining-philosophers.md)
|
||||
- [Broadcast Chat Application](concurrency/async-exercises/chat-app.md)
|
||||
- [Solutions](concurrency/async-exercises/solutions.md)
|
||||
|
||||
# Final Words
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Futures Control Flow
|
||||
|
||||
Futures can be combined together to produce concurrent compute flow graphs. We
|
||||
have already seen tasks, that function as independent threads of execution.
|
||||
|
||||
- [Join](control-flow/join.md)
|
||||
- [Select](control-flow/select.md)
|
||||
3
src/concurrency/async-control-flow.md
Normal file
3
src/concurrency/async-control-flow.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Channels and Control Flow
|
||||
|
||||
{{%segment outline}}
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 8
|
||||
---
|
||||
|
||||
# Async Channels
|
||||
|
||||
Several crates have support for asynchronous channels. For instance `tokio`:
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 4
|
||||
---
|
||||
|
||||
# Join
|
||||
|
||||
A join operation waits until all of a set of futures are ready, and returns a
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 5
|
||||
---
|
||||
|
||||
# Select
|
||||
|
||||
A select operation waits until any of a set of futures is ready, and responds to
|
||||
3
src/concurrency/async-exercises.md
Normal file
3
src/concurrency/async-exercises.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Exercises
|
||||
|
||||
{{%segment outline}}
|
||||
1
src/concurrency/async-exercises/afternoon.md
Normal file
1
src/concurrency/async-exercises/afternoon.md
Normal file
@@ -0,0 +1 @@
|
||||
# Exercises
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 30
|
||||
---
|
||||
|
||||
# Broadcast Chat Application
|
||||
|
||||
In this exercise, we want to use our new knowledge to implement a broadcast chat
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Dining Philosophers --- Async
|
||||
|
||||
See [dining philosophers](dining-philosophers.md) for a description of the
|
||||
@@ -11,17 +15,17 @@ code below to a file called `src/main.rs`, fill out the blanks, and test that
|
||||
<!-- File src/main.rs -->
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include dining-philosophers-async.rs:Philosopher}}
|
||||
{{#include dining-philosophers.rs:Philosopher}}
|
||||
// left_fork: ...
|
||||
// right_fork: ...
|
||||
// thoughts: ...
|
||||
}
|
||||
|
||||
{{#include dining-philosophers-async.rs:Philosopher-think}}
|
||||
{{#include dining-philosophers.rs:Philosopher-think}}
|
||||
|
||||
{{#include dining-philosophers-async.rs:Philosopher-eat}}
|
||||
{{#include dining-philosophers-async.rs:Philosopher-eat-body}}
|
||||
{{#include dining-philosophers-async.rs:Philosopher-eat-end}}
|
||||
{{#include dining-philosophers.rs:Philosopher-eat}}
|
||||
{{#include dining-philosophers.rs:Philosopher-eat-body}}
|
||||
{{#include dining-philosophers.rs:Philosopher-eat-end}}
|
||||
// Create forks
|
||||
|
||||
// Create philosophers
|
||||
@@ -1,17 +1,17 @@
|
||||
# Concurrency Afternoon Exercise
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Solutions
|
||||
|
||||
## Dining Philosophers --- Async
|
||||
|
||||
([back to exercise](dining-philosophers-async.md))
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include dining-philosophers-async.rs:solution}}
|
||||
{{#include dining-philosophers.rs:solution}}
|
||||
```
|
||||
|
||||
## Broadcast Chat Application
|
||||
|
||||
([back to exercise](chat-app.md))
|
||||
|
||||
_src/bin/server.rs_:
|
||||
|
||||
```rust,compile_fail
|
||||
@@ -1,10 +1,7 @@
|
||||
# Pitfalls of async/await
|
||||
# Pitfalls
|
||||
|
||||
Async / await provides convenient and efficient abstraction for concurrent
|
||||
asynchronous programming. However, the async/await model in Rust also comes with
|
||||
its share of pitfalls and footguns. We illustrate some of them in this chapter:
|
||||
its share of pitfalls and footguns. We illustrate some of them in this chapter.
|
||||
|
||||
- [Blocking the Executor](pitfalls/blocking-executor.md)
|
||||
- [Pin](pitfalls/pin.md)
|
||||
- [Async Traits](pitfalls/async-traits.md)
|
||||
- [Cancellation](pitfalls/cancellation.md)
|
||||
{{%segment outline}}
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 5
|
||||
---
|
||||
|
||||
# Async Traits
|
||||
|
||||
Async methods in traits are were stabilized only recently, in the 1.75 release.
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 10
|
||||
---
|
||||
|
||||
# Blocking the executor
|
||||
|
||||
Most async runtimes only allow IO tasks to run concurrently. This means that CPU
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 18
|
||||
---
|
||||
|
||||
# Cancellation
|
||||
|
||||
Dropping a future implies it can never be polled again. This is called
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# `Pin`
|
||||
|
||||
Async blocks and functions return types implementing the `Future` trait. The
|
||||
3
src/concurrency/async.md
Normal file
3
src/concurrency/async.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Async Basics
|
||||
|
||||
{{%segment outline}}
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 6
|
||||
---
|
||||
|
||||
# `async`/`await`
|
||||
|
||||
At a high level, async Rust code looks very much like "normal" sequential code:
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 4
|
||||
---
|
||||
|
||||
# Futures
|
||||
|
||||
[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html) is a trait,
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 10
|
||||
---
|
||||
|
||||
# Runtimes
|
||||
|
||||
A _runtime_ provides support for performing operations asynchronously (a
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 6
|
||||
---
|
||||
|
||||
# Tasks
|
||||
|
||||
Rust has a task system, which is a form of lightweight threading.
|
||||
@@ -1,32 +1,3 @@
|
||||
# Channels
|
||||
|
||||
Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts
|
||||
are connected via the channel, but you only see the end-points.
|
||||
|
||||
```rust,editable
|
||||
use std::sync::mpsc;
|
||||
|
||||
fn main() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tx.send(10).unwrap();
|
||||
tx.send(20).unwrap();
|
||||
|
||||
println!("Received: {:?}", rx.recv());
|
||||
println!("Received: {:?}", rx.recv());
|
||||
|
||||
let tx2 = tx.clone();
|
||||
tx2.send(30).unwrap();
|
||||
println!("Received: {:?}", rx.recv());
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- `mpsc` stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender`
|
||||
implement `Clone` (so you can make multiple producers) but `Receiver` does
|
||||
not.
|
||||
- `send()` and `recv()` return `Result`. If they return `Err`, it means the
|
||||
counterpart `Sender` or `Receiver` is dropped and the channel is closed.
|
||||
|
||||
</details>
|
||||
{{%segment outline}}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 8
|
||||
---
|
||||
|
||||
# Bounded Channels
|
||||
|
||||
With bounded (synchronous) channels, `send` can block the current thread:
|
||||
|
||||
36
src/concurrency/channels/senders-receivers.md
Normal file
36
src/concurrency/channels/senders-receivers.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
minutes: 9
|
||||
---
|
||||
|
||||
# Senders and Receivers
|
||||
|
||||
Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts
|
||||
are connected via the channel, but you only see the end-points.
|
||||
|
||||
```rust,editable
|
||||
use std::sync::mpsc;
|
||||
|
||||
fn main() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tx.send(10).unwrap();
|
||||
tx.send(20).unwrap();
|
||||
|
||||
println!("Received: {:?}", rx.recv());
|
||||
println!("Received: {:?}", rx.recv());
|
||||
|
||||
let tx2 = tx.clone();
|
||||
tx2.send(30).unwrap();
|
||||
println!("Received: {:?}", rx.recv());
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- `mpsc` stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender`
|
||||
implement `Clone` (so you can make multiple producers) but `Receiver` does
|
||||
not.
|
||||
- `send()` and `recv()` return `Result`. If they return `Err`, it means the
|
||||
counterpart `Sender` or `Receiver` is dropped and the channel is closed.
|
||||
|
||||
</details>
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# Unbounded Channels
|
||||
|
||||
You get an unbounded and asynchronous channel with `mpsc::channel()`:
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
# `Send` and `Sync`
|
||||
|
||||
How does Rust know to forbid shared access across threads? The answer is in two
|
||||
traits:
|
||||
|
||||
- [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread
|
||||
boundary.
|
||||
- [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread
|
||||
boundary.
|
||||
|
||||
`Send` and `Sync` are [unsafe traits][3]. The compiler will automatically derive
|
||||
them for your types as long as they only contain `Send` and `Sync` types. You
|
||||
can also implement them manually when you know it is valid.
|
||||
|
||||
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[2]: https://doc.rust-lang.org/std/marker/trait.Sync.html
|
||||
[3]: ../unsafe/unsafe-traits.md
|
||||
|
||||
<details>
|
||||
|
||||
- One can think of these traits as markers that the type has certain
|
||||
thread-safety properties.
|
||||
- They can be used in the generic constraints as normal traits.
|
||||
|
||||
</details>
|
||||
{{%segment outline}}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 6
|
||||
---
|
||||
|
||||
# Examples
|
||||
|
||||
## `Send + Sync`
|
||||
|
||||
29
src/concurrency/send-sync/marker-traits.md
Normal file
29
src/concurrency/send-sync/marker-traits.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# Marker Traits
|
||||
|
||||
How does Rust know to forbid shared access across threads? The answer is in two
|
||||
traits:
|
||||
|
||||
- [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread
|
||||
boundary.
|
||||
- [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread
|
||||
boundary.
|
||||
|
||||
`Send` and `Sync` are [unsafe traits][3]. The compiler will automatically derive
|
||||
them for your types as long as they only contain `Send` and `Sync` types. You
|
||||
can also implement them manually when you know it is valid.
|
||||
|
||||
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[2]: https://doc.rust-lang.org/std/marker/trait.Sync.html
|
||||
[3]: ../unsafe/unsafe-traits.md
|
||||
|
||||
<details>
|
||||
|
||||
- One can think of these traits as markers that the type has certain
|
||||
thread-safety properties.
|
||||
- They can be used in the generic constraints as normal traits.
|
||||
|
||||
</details>
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# `Send`
|
||||
|
||||
> A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# `Sync`
|
||||
|
||||
> A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple
|
||||
|
||||
3
src/concurrency/shared-state.md
Normal file
3
src/concurrency/shared-state.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Shared State
|
||||
|
||||
{{%segment outline}}
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 5
|
||||
---
|
||||
|
||||
# `Arc`
|
||||
|
||||
[`Arc<T>`][1] allows shared read-only access via `Arc::clone`:
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 8
|
||||
---
|
||||
|
||||
# Example
|
||||
|
||||
Let us see `Arc` and `Mutex` in action:
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 14
|
||||
---
|
||||
|
||||
# `Mutex`
|
||||
|
||||
[`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T`
|
||||
@@ -1,11 +0,0 @@
|
||||
# Shared State
|
||||
|
||||
Rust uses the type system to enforce synchronization of shared data. This is
|
||||
primarily done via two types:
|
||||
|
||||
- [`Arc<T>`][1], atomic reference counted `T`: handles sharing between threads
|
||||
and takes care to deallocate `T` when the last reference is dropped,
|
||||
- [`Mutex<T>`][2]: ensures mutually exclusive access to the `T` value.
|
||||
|
||||
[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html
|
||||
[2]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
|
||||
3
src/concurrency/sync-exercises.md
Normal file
3
src/concurrency/sync-exercises.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Exercises
|
||||
|
||||
{{%segment outline}}
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "comprehensive-rust"
|
||||
name = "sync-exercises"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "dining-philosophers"
|
||||
path = "concurrency/dining-philosophers.rs"
|
||||
path = "dining-philosophers.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "link-checker"
|
||||
path = "concurrency/link-checker.rs"
|
||||
path = "link-checker.rs"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.12.4", features = ["blocking"] }
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Dining Philosophers
|
||||
|
||||
The dining philosophers problem is a classic problem in concurrency:
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Multi-threaded Link Checker
|
||||
|
||||
Let us use our new knowledge to create a multi-threaded link checker. It should
|
||||
@@ -1,17 +1,17 @@
|
||||
# Concurrency Morning Exercise
|
||||
---
|
||||
minutes: 30
|
||||
---
|
||||
|
||||
# Solutions
|
||||
|
||||
## Dining Philosophers
|
||||
|
||||
([back to exercise](dining-philosophers.md))
|
||||
|
||||
```rust
|
||||
{{#include dining-philosophers.rs:solution}}
|
||||
```
|
||||
|
||||
## Link Checker
|
||||
|
||||
([back to exercise](link-checker.md))
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include link-checker.rs:solution}}
|
||||
```
|
||||
@@ -1,74 +1,3 @@
|
||||
# Threads
|
||||
|
||||
Rust threads work similarly to threads in other languages:
|
||||
|
||||
```rust,editable
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
thread::spawn(|| {
|
||||
for i in 1..10 {
|
||||
println!("Count in thread: {i}!");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
});
|
||||
|
||||
for i in 1..5 {
|
||||
println!("Main thread: {i}");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Threads are all daemon threads, the main thread does not wait for them.
|
||||
- Thread panics are independent of each other.
|
||||
- Panics can carry a payload, which can be unpacked with `downcast_ref`.
|
||||
|
||||
<details>
|
||||
|
||||
- Rust thread APIs look not too different from e.g. C++ ones.
|
||||
|
||||
- Run the example.
|
||||
- 5ms timing is loose enough that main and spawned threads stay mostly in
|
||||
lockstep.
|
||||
- Notice that the program ends before the spawned thread reaches 10!
|
||||
- This is because main ends the program and spawned threads do not make it
|
||||
persist.
|
||||
- Compare to pthreads/C++ std::thread/boost::thread if desired.
|
||||
|
||||
- How do we wait around for the spawned thread to complete?
|
||||
- [`thread::spawn`] returns a `JoinHandle`. Look at the docs.
|
||||
- `JoinHandle` has a [`.join()`] method that blocks.
|
||||
|
||||
- Use `let handle = thread::spawn(...)` and later `handle.join()` to wait for
|
||||
the thread to finish and have the program count all the way to 10.
|
||||
|
||||
- Now what if we want to return a value?
|
||||
- Look at docs again:
|
||||
- [`thread::spawn`]'s closure returns `T`
|
||||
- `JoinHandle` [`.join()`] returns `thread::Result<T>`
|
||||
|
||||
- Use the `Result` return value from `handle.join()` to get access to the
|
||||
returned value.
|
||||
|
||||
- Ok, what about the other case?
|
||||
- Trigger a panic in the thread. Note that this doesn't panic `main`.
|
||||
- Access the panic payload. This is a good time to talk about [`Any`].
|
||||
|
||||
- Now we can return values from threads! What about taking inputs?
|
||||
- Capture something by reference in the thread closure.
|
||||
- An error message indicates we must move it.
|
||||
- Move it in, see we can compute and then return a derived value.
|
||||
|
||||
- If we want to borrow?
|
||||
- Main kills child threads when it returns, but another function would just
|
||||
return and leave them running.
|
||||
- That would be stack use-after-return, which violates memory safety!
|
||||
- How do we avoid this? see next slide.
|
||||
|
||||
[`Any`]: https://doc.rust-lang.org/std/any/index.html
|
||||
[`thread::spawn`]: https://doc.rust-lang.org/std/thread/fn.spawn.html
|
||||
[`.join()`]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join
|
||||
|
||||
</details>
|
||||
{{%segment outline}}
|
||||
|
||||
78
src/concurrency/threads/plain.md
Normal file
78
src/concurrency/threads/plain.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
minutes: 15
|
||||
---
|
||||
|
||||
# Plain Threads
|
||||
|
||||
Rust threads work similarly to threads in other languages:
|
||||
|
||||
```rust,editable
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
thread::spawn(|| {
|
||||
for i in 1..10 {
|
||||
println!("Count in thread: {i}!");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
});
|
||||
|
||||
for i in 1..5 {
|
||||
println!("Main thread: {i}");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Threads are all daemon threads, the main thread does not wait for them.
|
||||
- Thread panics are independent of each other.
|
||||
- Panics can carry a payload, which can be unpacked with `downcast_ref`.
|
||||
|
||||
<details>
|
||||
|
||||
- Rust thread APIs look not too different from e.g. C++ ones.
|
||||
|
||||
- Run the example.
|
||||
- 5ms timing is loose enough that main and spawned threads stay mostly in
|
||||
lockstep.
|
||||
- Notice that the program ends before the spawned thread reaches 10!
|
||||
- This is because main ends the program and spawned threads do not make it
|
||||
persist.
|
||||
- Compare to pthreads/C++ std::thread/boost::thread if desired.
|
||||
|
||||
- How do we wait around for the spawned thread to complete?
|
||||
- [`thread::spawn`] returns a `JoinHandle`. Look at the docs.
|
||||
- `JoinHandle` has a [`.join()`] method that blocks.
|
||||
|
||||
- Use `let handle = thread::spawn(...)` and later `handle.join()` to wait for
|
||||
the thread to finish and have the program count all the way to 10.
|
||||
|
||||
- Now what if we want to return a value?
|
||||
- Look at docs again:
|
||||
- [`thread::spawn`]'s closure returns `T`
|
||||
- `JoinHandle` [`.join()`] returns `thread::Result<T>`
|
||||
|
||||
- Use the `Result` return value from `handle.join()` to get access to the
|
||||
returned value.
|
||||
|
||||
- Ok, what about the other case?
|
||||
- Trigger a panic in the thread. Note that this doesn't panic `main`.
|
||||
- Access the panic payload. This is a good time to talk about [`Any`].
|
||||
|
||||
- Now we can return values from threads! What about taking inputs?
|
||||
- Capture something by reference in the thread closure.
|
||||
- An error message indicates we must move it.
|
||||
- Move it in, see we can compute and then return a derived value.
|
||||
|
||||
- If we want to borrow?
|
||||
- Main kills child threads when it returns, but another function would just
|
||||
return and leave them running.
|
||||
- That would be stack use-after-return, which violates memory safety!
|
||||
- How do we avoid this? see next slide.
|
||||
|
||||
[`Any`]: https://doc.rust-lang.org/std/any/index.html
|
||||
[`thread::spawn`]: https://doc.rust-lang.org/std/thread/fn.spawn.html
|
||||
[`.join()`]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join
|
||||
|
||||
</details>
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 13
|
||||
---
|
||||
|
||||
# Scoped Threads
|
||||
|
||||
Normal threads cannot borrow from their environment:
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
session: Afternoon
|
||||
target_minutes: 180
|
||||
---
|
||||
|
||||
# Async Rust
|
||||
# Welcome
|
||||
|
||||
"Async" is a concurrency model where multiple tasks are executed concurrently by
|
||||
executing each task until it would block, then switching to another task that is
|
||||
@@ -27,3 +28,7 @@ available.
|
||||
- JavaScript's `Promise` is similar, but again callback-based. The language
|
||||
runtime implements the event loop, so many of the details of Promise
|
||||
resolution are hidden.
|
||||
|
||||
## Schedule
|
||||
|
||||
{{%session outline}}
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
course: Concurrency
|
||||
session: Morning
|
||||
target_minutes: 180
|
||||
---
|
||||
|
||||
# Welcome to Concurrency in Rust
|
||||
@@ -12,6 +13,10 @@ The Rust type system plays an important role in making many concurrency bugs
|
||||
compile time bugs. This is often referred to as _fearless concurrency_ since you
|
||||
can rely on the compiler to ensure correctness at runtime.
|
||||
|
||||
## Schedule
|
||||
|
||||
{{%session outline}}
|
||||
|
||||
<details>
|
||||
|
||||
- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Exercises
|
||||
|
||||
To practice your Async Rust skills, we have again two exercises for you:
|
||||
|
||||
- Dining philosophers: we already saw this problem in the morning. This time you
|
||||
are going to implement it with Async Rust.
|
||||
|
||||
- A Broadcast Chat Application: this is a larger project that allows you
|
||||
experiment with more advanced Async Rust features.
|
||||
|
||||
<details>
|
||||
|
||||
After looking at the exercises, you can look at the [solutions] provided.
|
||||
|
||||
[solutions]: solutions-afternoon.md
|
||||
|
||||
</details>
|
||||
@@ -70,6 +70,8 @@ cargo add tokio --features full
|
||||
cargo run
|
||||
```
|
||||
|
||||
{{%course outline Concurrency}}
|
||||
|
||||
## Format
|
||||
|
||||
The course is meant to be very interactive and we recommend letting the
|
||||
|
||||
Reference in New Issue
Block a user