]> git.lizzy.rs Git - rust.git/commitdiff
trpl: Improve clarity in Concurrency
authorcritiqjo <john.ch.fr@gmail.com>
Thu, 17 Sep 2015 15:00:17 +0000 (20:30 +0530)
committercritiqjo <john.ch.fr@gmail.com>
Sat, 19 Sep 2015 15:41:17 +0000 (21:11 +0530)
src/doc/trpl/concurrency.md

index e00fe75013e29cf31fac8e2bb1f8d1abade5806d..7028bade6deae557909b680cdbdf33cc707f4c1e 100644 (file)
@@ -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.
-
-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.
+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 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