diff --git a/src/concurrency.md b/src/concurrency.md index de6809721530..18f8f1f8d891 100644 --- a/src/concurrency.md +++ b/src/concurrency.md @@ -11,3 +11,13 @@ channels. 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. + +
+ +- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc. +- The type system gives us safety for concurrency without any special features. +- The same tools that help with "concurrent" access in a single thread (e.g., a + called function that might mutate an argument or save references to it to read + later) save us from multi-threading issues. + +
diff --git a/src/concurrency/threads.md b/src/concurrency/threads.md index 2f7456ab65b8..432231c876d1 100644 --- a/src/concurrency/threads.md +++ b/src/concurrency/threads.md @@ -27,19 +27,48 @@ fn main() {
-Key points: +- Rust thread APIs look not too different from e.g. C++ ones. -- Notice that the thread is stopped before it reaches 10 --- the main thread is - not waiting. +- 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. + 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` + +- 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`]. -- Trigger a panic in the thread, notice how this doesn't affect `main`. +- 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. -- Use the `Result` return value from `handle.join()` to get access to the panic - payload. This is a good time to talk about [`Any`]. +- 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