]> git.proxmox.com Git - rustc.git/blame - src/doc/book/2018-edition/src/ch16-03-shared-state.md
New upstream version 1.31.0~beta.4+dfsg1
[rustc.git] / src / doc / book / 2018-edition / src / ch16-03-shared-state.md
CommitLineData
83c7162d
XL
1## Shared-State Concurrency
2
3Message passing is a fine way of handling concurrency, but it’s not the only
4one. Consider this part of the slogan from the Go language documentation again:
5“communicate by sharing memory.”
6
7What would communicating by sharing memory look like? In addition, why would
8message-passing enthusiasts not use it and do the opposite instead?
9
10In a way, channels in any programming language are similar to single ownership,
11because once you transfer a value down a channel, you should no longer use that
12value. Shared memory concurrency is like multiple ownership: multiple threads
13can access the same memory location at the same time. As you saw in Chapter 15,
14where smart pointers made multiple ownership possible, multiple ownership can
15add complexity because these different owners need managing. Rust’s type system
16and ownership rules greatly assist in getting this management correct. For an
17example, let’s look at mutexes, one of the more common concurrency primitives
18for shared memory.
19
20### Using Mutexes to Allow Access to Data from One Thread at a Time
21
22*Mutex* is an abbreviation for *mutual exclusion*, as in, a mutex allows only
23one thread to access some data at any given time. To access the data in a
24mutex, a thread must first signal that it wants access by asking to acquire the
25mutex’s *lock*. The lock is a data structure that is part of the mutex that
26keeps track of who currently has exclusive access to the data. Therefore, the
27mutex is described as *guarding* the data it holds via the locking system.
28
29Mutexes have a reputation for being difficult to use because you have to
30remember two rules:
31
32* You must attempt to acquire the lock before using the data.
33* When you’re done with the data that the mutex guards, you must unlock the
34 data so other threads can acquire the lock.
35
36For a real-world metaphor for a mutex, imagine a panel discussion at a
37conference with only one microphone. Before a panelist can speak, they have to
38ask or signal that they want to use the microphone. When they get the
39microphone, they can talk for as long as they want to and then hand the
40microphone to the next panelist who requests to speak. If a panelist forgets to
41hand the microphone off when they’re finished with it, no one else is able to
42speak. If management of the shared microphone goes wrong, the panel won’t work
43as planned!
44
45Management of mutexes can be incredibly tricky to get right, which is why so
46many people are enthusiastic about channels. However, thanks to Rust’s type
47system and ownership rules, you can’t get locking and unlocking wrong.
48
49#### The API of `Mutex<T>`
50
51As an example of how to use a mutex, let’s start by using a mutex in a
52single-threaded context, as shown in Listing 16-12:
53
54<span class="filename">Filename: src/main.rs</span>
55
56```rust
57use std::sync::Mutex;
58
59fn main() {
60 let m = Mutex::new(5);
61
62 {
63 let mut num = m.lock().unwrap();
64 *num = 6;
65 }
66
67 println!("m = {:?}", m);
68}
69```
70
71<span class="caption">Listing 16-12: Exploring the API of `Mutex<T>` in a
72single-threaded context for simplicity</span>
73
74As with many types, we create a `Mutex<T>` using the associated function `new`.
75To access the data inside the mutex, we use the `lock` method to acquire the
76lock. This call will block the current thread so it can’t do any work until
77it’s our turn to have the lock.
78
79The call to `lock` would fail if another thread holding the lock panicked. In
80that case, no one would ever be able to get the lock, so we’ve chosen to
81`unwrap` and have this thread panic if we’re in that situation.
82
83After we’ve acquired the lock, we can treat the return value, named `num` in
84this case, as a mutable reference to the data inside. The type system ensures
85that we acquire a lock before using the value in `m`: `Mutex<i32>` is not an
86`i32`, so we *must* acquire the lock to be able to use the `i32` value. We
87can’t forget; the type system won’t let us access the inner `i32` otherwise.
88
89As you might suspect, `Mutex<T>` is a smart pointer. More accurately, the call
90to `lock` *returns* a smart pointer called `MutexGuard`. This smart pointer
91implements `Deref` to point at our inner data; the smart pointer also has a
92`Drop` implementation that releases the lock automatically when a `MutexGuard`
93goes out of scope, which happens at the end of the inner scope in Listing
9416-12. As a result, we don’t risk forgetting to release the lock and blocking
95the mutex from being used by other threads because the lock release happens
96automatically.
97
98After dropping the lock, we can print the mutex value and see that we were able
99to change the inner `i32` to 6.
100
101#### Sharing a `Mutex<T>` Between Multiple Threads
102
103Now, let’s try to share a value between multiple threads using `Mutex<T>`.
104We’ll spin up 10 threads and have them each increment a counter value by 1, so
105the counter goes from 0 to 10. Note that the next few examples will have
106compiler errors, and we’ll use those errors to learn more about using
107`Mutex<T>` and how Rust helps us use it correctly. Listing 16-13 has our
108starting example:
109
110<span class="filename">Filename: src/main.rs</span>
111
0bf4aa26 112```rust,ignore,does_not_compile
83c7162d
XL
113use std::sync::Mutex;
114use std::thread;
115
116fn main() {
117 let counter = Mutex::new(0);
118 let mut handles = vec![];
119
120 for _ in 0..10 {
121 let handle = thread::spawn(move || {
122 let mut num = counter.lock().unwrap();
123
124 *num += 1;
125 });
126 handles.push(handle);
127 }
128
129 for handle in handles {
130 handle.join().unwrap();
131 }
132
133 println!("Result: {}", *counter.lock().unwrap());
134}
135```
136
137<span class="caption">Listing 16-13: Ten threads each increment a counter
138guarded by a `Mutex<T>`</span>
139
140We create a `counter` variable to hold an `i32` inside a `Mutex<T>`, as we
141did in Listing 16-12. Next, we create 10 threads by iterating over a range
142of numbers. We use `thread::spawn` and give all the threads the same closure,
143one that moves the counter into the thread, acquires a lock on the `Mutex<T>`
144by calling the `lock` method, and then adds 1 to the value in the mutex. When a
145thread finishes running its closure, `num` will go out of scope and release the
146lock so another thread can acquire it.
147
148In the main thread, we collect all the join handles. Then, as we did in Listing
14916-2, we call `join` on each handle to make sure all the threads finish. At
150that point, the main thread will acquire the lock and print the result of this
151program.
152
153We hinted that this example wouldn’t compile. Now let’s find out why!
154
155```text
156error[E0382]: capture of moved value: `counter`
157 --> src/main.rs:10:27
158 |
1599 | let handle = thread::spawn(move || {
160 | ------- value moved (into closure) here
16110 | let mut num = counter.lock().unwrap();
162 | ^^^^^^^ value captured here after move
163 |
164 = note: move occurs because `counter` has type `std::sync::Mutex<i32>`,
165 which does not implement the `Copy` trait
166
167error[E0382]: use of moved value: `counter`
168 --> src/main.rs:21:29
169 |
1709 | let handle = thread::spawn(move || {
171 | ------- value moved (into closure) here
172...
17321 | println!("Result: {}", *counter.lock().unwrap());
174 | ^^^^^^^ value used here after move
175 |
176 = note: move occurs because `counter` has type `std::sync::Mutex<i32>`,
177 which does not implement the `Copy` trait
178
179error: aborting due to 2 previous errors
180```
181
182The error message states that the `counter` value is moved into the closure and
183then captured when we call `lock`. That description sounds like what we wanted,
184but it’s not allowed!
185
186Let’s figure this out by simplifying the program. Instead of making 10 threads
187in a `for` loop, let’s just make two threads without a loop and see what
188happens. Replace the first `for` loop in Listing 16-13 with this code instead:
189
0bf4aa26 190```rust,ignore,does_not_compile
83c7162d
XL
191use std::sync::Mutex;
192use std::thread;
193
194fn main() {
195 let counter = Mutex::new(0);
196 let mut handles = vec![];
197
198 let handle = thread::spawn(move || {
199 let mut num = counter.lock().unwrap();
200
201 *num += 1;
202 });
203 handles.push(handle);
204
205 let handle2 = thread::spawn(move || {
206 let mut num2 = counter.lock().unwrap();
207
208 *num2 += 1;
209 });
210 handles.push(handle2);
211
212 for handle in handles {
213 handle.join().unwrap();
214 }
215
216 println!("Result: {}", *counter.lock().unwrap());
217}
218```
219
220We make two threads and change the variable names used with the second thread
221to `handle2` and `num2`. When we run the code this time, compiling gives us the
222following:
223
224```text
225error[E0382]: capture of moved value: `counter`
226 --> src/main.rs:16:24
227 |
2288 | let handle = thread::spawn(move || {
229 | ------- value moved (into closure) here
230...
23116 | let mut num2 = counter.lock().unwrap();
232 | ^^^^^^^ value captured here after move
233 |
234 = note: move occurs because `counter` has type `std::sync::Mutex<i32>`,
235 which does not implement the `Copy` trait
236
237error[E0382]: use of moved value: `counter`
238 --> src/main.rs:26:29
239 |
2408 | let handle = thread::spawn(move || {
241 | ------- value moved (into closure) here
242...
24326 | println!("Result: {}", *counter.lock().unwrap());
244 | ^^^^^^^ value used here after move
245 |
246 = note: move occurs because `counter` has type `std::sync::Mutex<i32>`,
247 which does not implement the `Copy` trait
248
249error: aborting due to 2 previous errors
250```
251
252Aha! The first error message indicates that `counter` is moved into the closure
253for the thread associated with `handle`. That move is preventing us from
254capturing `counter` when we try to call `lock` on it and store the result in
255`num2` in the second thread! So Rust is telling us that we can’t move ownership
256of `counter` into multiple threads. This was hard to see earlier because our
257threads were in a loop, and Rust can’t point to different threads in different
258iterations of the loop. Let’s fix the compiler error with a multiple-ownership
259method we discussed in Chapter 15.
260
261#### Multiple Ownership with Multiple Threads
262
263In Chapter 15, we gave a value multiple owners by using the smart pointer
264`Rc<T>` to create a reference counted value. Let’s do the same here and see
265what happens. We’ll wrap the `Mutex<T>` in `Rc<T>` in Listing 16-14 and clone
266the `Rc<T>` before moving ownership to the thread. Now that we’ve seen the
267errors, we’ll also switch back to using the `for` loop, and we’ll keep the
268`move` keyword with the closure.
269
270<span class="filename">Filename: src/main.rs</span>
271
0bf4aa26 272```rust,ignore,does_not_compile
83c7162d
XL
273use std::rc::Rc;
274use std::sync::Mutex;
275use std::thread;
276
277fn main() {
278 let counter = Rc::new(Mutex::new(0));
279 let mut handles = vec![];
280
281 for _ in 0..10 {
282 let counter = Rc::clone(&counter);
283 let handle = thread::spawn(move || {
284 let mut num = counter.lock().unwrap();
285
286 *num += 1;
287 });
288 handles.push(handle);
289 }
290
291 for handle in handles {
292 handle.join().unwrap();
293 }
294
295 println!("Result: {}", *counter.lock().unwrap());
296}
297```
298
299<span class="caption">Listing 16-14: Attempting to use `Rc<T>` to allow
300multiple threads to own the `Mutex<T>`</span>
301
302Once again, we compile and get... different errors! The compiler is teaching us
303a lot.
304
305```text
306error[E0277]: the trait bound `std::rc::Rc<std::sync::Mutex<i32>>:
307std::marker::Send` is not satisfied in `[closure@src/main.rs:11:36:
30815:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`
309 --> src/main.rs:11:22
310 |
31111 | let handle = thread::spawn(move || {
312 | ^^^^^^^^^^^^^ `std::rc::Rc<std::sync::Mutex<i32>>`
313cannot be sent between threads safely
314 |
315 = help: within `[closure@src/main.rs:11:36: 15:10
316counter:std::rc::Rc<std::sync::Mutex<i32>>]`, the trait `std::marker::Send` is
317not implemented for `std::rc::Rc<std::sync::Mutex<i32>>`
318 = note: required because it appears within the type
319`[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`
320 = note: required by `std::thread::spawn`
321```
322
323Wow, that error message is very wordy! Here are some important parts to focus
324on: the first inline error says `` `std::rc::Rc<std::sync::Mutex<i32>>` cannot
325be sent between threads safely ``. The reason for this is in the next important
326part to focus on, the error message. The distilled error message says `` the
327trait bound `Send` is not satisfied ``. We’ll talk about `Send` in the next
328section: it’s one of the traits that ensures the types we use with threads are
329meant for use in concurrent situations.
330
331Unfortunately, `Rc<T>` is not safe to share across threads. When `Rc<T>`
332manages the reference count, it adds to the count for each call to `clone` and
333subtracts from the count when each clone is dropped. But it doesn’t use any
334concurrency primitives to make sure that changes to the count can’t be
335interrupted by another thread. This could lead to wrong counts—subtle bugs that
336could in turn lead to memory leaks or a value being dropped before we’re done
337with it. What we need is a type exactly like `Rc<T>` but one that makes changes
338to the reference count in a thread-safe way.
339
340#### Atomic Reference Counting with `Arc<T>`
341
342Fortunately, `Arc<T>` *is* a type like `Rc<T>` that is safe to use in
343concurrent situations. The *a* stands for *atomic*, meaning it’s an *atomically
344reference counted* type. Atomics are an additional kind of concurrency
345primitive that we won’t cover in detail here: see the standard library
346documentation for `std::sync::atomic` for more details. At this point, you just
347need to know that atomics work like primitive types but are safe to share
348across threads.
349
350You might then wonder why all primitive types aren’t atomic and why standard
351library types aren’t implemented to use `Arc<T>` by default. The reason is that
352thread safety comes with a performance penalty that you only want to pay when
353you really need to. If you’re just performing operations on values within a
354single thread, your code can run faster if it doesn’t have to enforce the
355guarantees atomics provide.
356
357Let’s return to our example: `Arc<T>` and `Rc<T>` have the same API, so we fix
358our program by changing the `use` line, the call to `new`, and the call to
359`clone`. The code in Listing 16-15 will finally compile and run:
360
361<span class="filename">Filename: src/main.rs</span>
362
363```rust
364use std::sync::{Mutex, Arc};
365use std::thread;
366
367fn main() {
368 let counter = Arc::new(Mutex::new(0));
369 let mut handles = vec![];
370
371 for _ in 0..10 {
372 let counter = Arc::clone(&counter);
373 let handle = thread::spawn(move || {
374 let mut num = counter.lock().unwrap();
375
376 *num += 1;
377 });
378 handles.push(handle);
379 }
380
381 for handle in handles {
382 handle.join().unwrap();
383 }
384
385 println!("Result: {}", *counter.lock().unwrap());
386}
387```
388
389<span class="caption">Listing 16-15: Using an `Arc<T>` to wrap the `Mutex<T>`
390to be able to share ownership across multiple threads</span>
391
392This code will print the following:
393
394```text
395Result: 10
396```
397
398We did it! We counted from 0 to 10, which may not seem very impressive, but it
399did teach us a lot about `Mutex<T>` and thread safety. You could also use this
400program’s structure to do more complicated operations than just incrementing a
401counter. Using this strategy, you can divide a calculation into independent
402parts, split those parts across threads, and then use a `Mutex<T>` to have each
403thread update the final result with its part.
404
405### Similarities Between `RefCell<T>`/`Rc<T>` and `Mutex<T>`/`Arc<T>`
406
407You might have noticed that `counter` is immutable but we could get a mutable
408reference to the value inside it; this means `Mutex<T>` provides interior
409mutability, as the `Cell` family does. In the same way we used `RefCell<T>` in
410Chapter 15 to allow us to mutate contents inside an `Rc<T>`, we use `Mutex<T>`
411to mutate contents inside an `Arc<T>`.
412
413Another detail to note is that Rust can’t protect you from all kinds of logic
414errors when you use `Mutex<T>`. Recall in Chapter 15 that using `Rc<T>` came
415with the risk of creating reference cycles, where two `Rc<T>` values refer to
416each other, causing memory leaks. Similarly, `Mutex<T>` comes with the risk of
417creating *deadlocks*. These occur when an operation needs to lock two resources
418and two threads have each acquired one of the locks, causing them to wait for
419each other forever. If you’re interested in deadlocks, try creating a Rust
420program that has a deadlock; then research deadlock mitigation strategies for
421mutexes in any language and have a go at implementing them in Rust. The
422standard library API documentation for `Mutex<T>` and `MutexGuard` offers
423useful information.
424
425We’ll round out this chapter by talking about the `Send` and `Sync` traits and
426how we can use them with custom types.