trpl: Improve clarity in Concurrency
This commit is contained in:
parent
89faafcd67
commit
a0f214e257
@ -26,8 +26,8 @@ to help us make sense of code that can possibly be concurrent.
|
||||
### `Send`
|
||||
|
||||
The first trait we're going to talk about is
|
||||
[`Send`](../std/marker/trait.Send.html). When a type `T` implements `Send`, it indicates
|
||||
to the compiler that something of this type is able to have ownership transferred
|
||||
[`Send`](../std/marker/trait.Send.html). When a type `T` implements `Send`, it
|
||||
indicates that something of this type is able to have ownership transferred
|
||||
safely between threads.
|
||||
|
||||
This is important to enforce certain restrictions. For example, if we have a
|
||||
@ -42,13 +42,19 @@ us enforce that it can't leave the current thread.
|
||||
### `Sync`
|
||||
|
||||
The second of these traits is called [`Sync`](../std/marker/trait.Sync.html).
|
||||
When a type `T` implements `Sync`, it indicates to the compiler that something
|
||||
When a type `T` implements `Sync`, it indicates that something
|
||||
of this type has no possibility of introducing memory unsafety when used from
|
||||
multiple threads concurrently.
|
||||
multiple threads concurrently through shared references. This implies that
|
||||
types which don't have [interior mutability](mutability.html) are inherently
|
||||
`Sync`, which includes simple primitive types (like `u8`) and aggregate types
|
||||
containing them.
|
||||
|
||||
For example, sharing immutable data with an atomic reference count is
|
||||
threadsafe. Rust provides a type like this, `Arc<T>`, and it implements `Sync`,
|
||||
so it is safe to share between threads.
|
||||
For sharing references across threads, Rust provides a wrapper type called
|
||||
`Arc<T>`. `Arc<T>` implements `Send` and `Sync` if and only if `T` implements
|
||||
both `Send` and `Sync`. For example, an object of type `Arc<RefCell<U>>` cannot
|
||||
be transferred across threads because
|
||||
[`RefCell`](choosing-your-guarantees.html#refcell%3Ct%3E) does not implement
|
||||
`Sync`, consequently `Arc<RefCell<U>>` would not implement `Send`.
|
||||
|
||||
These two traits allow you to use the type system to make strong guarantees
|
||||
about the properties of your code under concurrency. Before we demonstrate
|
||||
@ -70,7 +76,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
The `thread::spawn()` method accepts a closure, which is executed in a
|
||||
The `thread::spawn()` method accepts a [closure](closures.html), which is executed in a
|
||||
new thread. It returns a handle to the thread, that can be used to
|
||||
wait for the child thread to finish and extract its result:
|
||||
|
||||
@ -215,29 +221,18 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Note that the value of `i` is bound (copied) to the closure and not shared
|
||||
among the threads.
|
||||
|
||||
If we'd tried to use `Mutex<T>` without wrapping it in an `Arc<T>` we would have
|
||||
seen another error like:
|
||||
|
||||
```text
|
||||
error: the trait `core::marker::Send` is not implemented for the type `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` [E0277]
|
||||
thread::spawn(move || {
|
||||
^~~~~~~~~~~~~
|
||||
note: `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` cannot be sent between threads safely
|
||||
thread::spawn(move || {
|
||||
^~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
You see, [`Mutex`](../std/sync/struct.Mutex.html) has a
|
||||
[`lock`](../std/sync/struct.Mutex.html#method.lock)
|
||||
method which has this signature:
|
||||
Also note that [`lock`](../std/sync/struct.Mutex.html#method.lock) method of
|
||||
[`Mutex`](../std/sync/struct.Mutex.html) has this signature:
|
||||
|
||||
```ignore
|
||||
fn lock(&self) -> LockResult<MutexGuard<T>>
|
||||
```
|
||||
|
||||
and because `Send` is not implemented for `MutexGuard<T>`, we couldn't have
|
||||
transferred the guard across thread boundaries on it's own.
|
||||
and because `Send` is not implemented for `MutexGuard<T>`, the guard cannot
|
||||
cross thread boundaries, ensuring thread-locality of lock acquire and release.
|
||||
|
||||
Let's examine the body of the thread more closely:
|
||||
|
||||
@ -317,22 +312,24 @@ use std::sync::mpsc;
|
||||
fn main() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
for _ in 0..10 {
|
||||
for i in 0..10 {
|
||||
let tx = tx.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let answer = 42;
|
||||
let answer = i * i;
|
||||
|
||||
tx.send(answer);
|
||||
});
|
||||
}
|
||||
|
||||
rx.recv().ok().expect("Could not receive answer");
|
||||
for _ in 0..10 {
|
||||
println!("{}", rx.recv().unwrap());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A `u32` is `Send` because we can make a copy. So we create a thread, ask it to calculate
|
||||
the answer, and then it `send()`s us the answer over the channel.
|
||||
Here we create 10 threads, asking each to calculate the square of a number (`i`
|
||||
at the time of `spawn()`), and then `send()` back the answer over the channel.
|
||||
|
||||
|
||||
## Panics
|
||||
|
Loading…
Reference in New Issue
Block a user