]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## Shared-State Concurrency |
2 | ||
3 | Message passing is a fine way of handling concurrency, but it’s not the only | |
4 | one. Consider this part of the slogan from the Go language documentation again: | |
e1599b0c | 5 | “do not communicate by sharing memory.” |
13cf67c4 XL |
6 | |
7 | What would communicating by sharing memory look like? In addition, why would | |
8 | message-passing enthusiasts not use it and do the opposite instead? | |
9 | ||
10 | In a way, channels in any programming language are similar to single ownership, | |
11 | because once you transfer a value down a channel, you should no longer use that | |
12 | value. Shared memory concurrency is like multiple ownership: multiple threads | |
13 | can access the same memory location at the same time. As you saw in Chapter 15, | |
14 | where smart pointers made multiple ownership possible, multiple ownership can | |
15 | add complexity because these different owners need managing. Rust’s type system | |
16 | and ownership rules greatly assist in getting this management correct. For an | |
17 | example, let’s look at mutexes, one of the more common concurrency primitives | |
18 | for 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 | |
23 | one thread to access some data at any given time. To access the data in a | |
24 | mutex, a thread must first signal that it wants access by asking to acquire the | |
25 | mutex’s *lock*. The lock is a data structure that is part of the mutex that | |
26 | keeps track of who currently has exclusive access to the data. Therefore, the | |
27 | mutex is described as *guarding* the data it holds via the locking system. | |
28 | ||
29 | Mutexes have a reputation for being difficult to use because you have to | |
30 | remember 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 | ||
36 | For a real-world metaphor for a mutex, imagine a panel discussion at a | |
37 | conference with only one microphone. Before a panelist can speak, they have to | |
38 | ask or signal that they want to use the microphone. When they get the | |
39 | microphone, they can talk for as long as they want to and then hand the | |
40 | microphone to the next panelist who requests to speak. If a panelist forgets to | |
41 | hand the microphone off when they’re finished with it, no one else is able to | |
42 | speak. If management of the shared microphone goes wrong, the panel won’t work | |
43 | as planned! | |
44 | ||
45 | Management of mutexes can be incredibly tricky to get right, which is why so | |
46 | many people are enthusiastic about channels. However, thanks to Rust’s type | |
47 | system and ownership rules, you can’t get locking and unlocking wrong. | |
48 | ||
49 | #### The API of `Mutex<T>` | |
50 | ||
51 | As an example of how to use a mutex, let’s start by using a mutex in a | |
52 | single-threaded context, as shown in Listing 16-12: | |
53 | ||
54 | <span class="filename">Filename: src/main.rs</span> | |
55 | ||
56 | ```rust | |
74b04a01 | 57 | {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-12/src/main.rs}} |
13cf67c4 XL |
58 | ``` |
59 | ||
60 | <span class="caption">Listing 16-12: Exploring the API of `Mutex<T>` in a | |
61 | single-threaded context for simplicity</span> | |
62 | ||
63 | As with many types, we create a `Mutex<T>` using the associated function `new`. | |
64 | To access the data inside the mutex, we use the `lock` method to acquire the | |
65 | lock. This call will block the current thread so it can’t do any work until | |
66 | it’s our turn to have the lock. | |
67 | ||
68 | The call to `lock` would fail if another thread holding the lock panicked. In | |
69 | that case, no one would ever be able to get the lock, so we’ve chosen to | |
70 | `unwrap` and have this thread panic if we’re in that situation. | |
71 | ||
72 | After we’ve acquired the lock, we can treat the return value, named `num` in | |
73 | this case, as a mutable reference to the data inside. The type system ensures | |
74 | that we acquire a lock before using the value in `m`: `Mutex<i32>` is not an | |
75 | `i32`, so we *must* acquire the lock to be able to use the `i32` value. We | |
76 | can’t forget; the type system won’t let us access the inner `i32` otherwise. | |
77 | ||
78 | As you might suspect, `Mutex<T>` is a smart pointer. More accurately, the call | |
532ac7d7 XL |
79 | to `lock` *returns* a smart pointer called `MutexGuard`, wrapped in a |
80 | `LockResult` that we handled with the call to `unwrap`. The `MutexGuard` smart | |
81 | pointer implements `Deref` to point at our inner data; the smart pointer also | |
82 | has a `Drop` implementation that releases the lock automatically when a | |
83 | `MutexGuard` goes out of scope, which happens at the end of the inner scope in | |
84 | Listing 16-12. As a result, we don’t risk forgetting to release the lock and | |
85 | blocking the mutex from being used by other threads because the lock release | |
86 | happens automatically. | |
13cf67c4 XL |
87 | |
88 | After dropping the lock, we can print the mutex value and see that we were able | |
89 | to change the inner `i32` to 6. | |
90 | ||
91 | #### Sharing a `Mutex<T>` Between Multiple Threads | |
92 | ||
93 | Now, let’s try to share a value between multiple threads using `Mutex<T>`. | |
94 | We’ll spin up 10 threads and have them each increment a counter value by 1, so | |
60c5eb7d XL |
95 | the counter goes from 0 to 10. The next example in Listing 16-13 will have |
96 | a compiler error, and we’ll use that error to learn more about using | |
97 | `Mutex<T>` and how Rust helps us use it correctly. | |
13cf67c4 XL |
98 | |
99 | <span class="filename">Filename: src/main.rs</span> | |
100 | ||
101 | ```rust,ignore,does_not_compile | |
74b04a01 | 102 | {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-13/src/main.rs}} |
13cf67c4 XL |
103 | ``` |
104 | ||
105 | <span class="caption">Listing 16-13: Ten threads each increment a counter | |
106 | guarded by a `Mutex<T>`</span> | |
107 | ||
108 | We create a `counter` variable to hold an `i32` inside a `Mutex<T>`, as we | |
109 | did in Listing 16-12. Next, we create 10 threads by iterating over a range | |
110 | of numbers. We use `thread::spawn` and give all the threads the same closure, | |
111 | one that moves the counter into the thread, acquires a lock on the `Mutex<T>` | |
112 | by calling the `lock` method, and then adds 1 to the value in the mutex. When a | |
113 | thread finishes running its closure, `num` will go out of scope and release the | |
114 | lock so another thread can acquire it. | |
115 | ||
116 | In the main thread, we collect all the join handles. Then, as we did in Listing | |
117 | 16-2, we call `join` on each handle to make sure all the threads finish. At | |
118 | that point, the main thread will acquire the lock and print the result of this | |
119 | program. | |
120 | ||
121 | We hinted that this example wouldn’t compile. Now let’s find out why! | |
122 | ||
f035d41b | 123 | ```console |
74b04a01 | 124 | {{#include ../listings/ch16-fearless-concurrency/listing-16-13/output.txt}} |
13cf67c4 XL |
125 | ``` |
126 | ||
60c5eb7d XL |
127 | The error message states that the `counter` value was moved in the previous |
128 | iteration of the loop. So Rust is telling us that we can’t move the ownership | |
129 | of lock `counter` into multiple threads. Let’s fix the compiler error with a | |
130 | multiple-ownership method we discussed in Chapter 15. | |
13cf67c4 XL |
131 | |
132 | #### Multiple Ownership with Multiple Threads | |
133 | ||
134 | In Chapter 15, we gave a value multiple owners by using the smart pointer | |
135 | `Rc<T>` to create a reference counted value. Let’s do the same here and see | |
136 | what happens. We’ll wrap the `Mutex<T>` in `Rc<T>` in Listing 16-14 and clone | |
5869c6ff | 137 | the `Rc<T>` before moving ownership to the thread. |
13cf67c4 XL |
138 | |
139 | <span class="filename">Filename: src/main.rs</span> | |
140 | ||
141 | ```rust,ignore,does_not_compile | |
74b04a01 | 142 | {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-14/src/main.rs}} |
13cf67c4 XL |
143 | ``` |
144 | ||
145 | <span class="caption">Listing 16-14: Attempting to use `Rc<T>` to allow | |
146 | multiple threads to own the `Mutex<T>`</span> | |
147 | ||
148 | Once again, we compile and get... different errors! The compiler is teaching us | |
149 | a lot. | |
150 | ||
f035d41b | 151 | ```console |
74b04a01 | 152 | {{#include ../listings/ch16-fearless-concurrency/listing-16-14/output.txt}} |
13cf67c4 XL |
153 | ``` |
154 | ||
60c5eb7d XL |
155 | Wow, that error message is very wordy! Here’s the important part to focus |
156 | on: `` `Rc<Mutex<i32>>` cannot be sent between threads safely ``. The compiler | |
157 | is also telling us the reason why: ``the trait `Send` is not implemented for | |
158 | `Rc<Mutex<i32>>` ``. We’ll talk about `Send` in the next section: it’s one of | |
159 | the traits that ensures the types we use with threads are meant for use in | |
160 | concurrent situations. | |
13cf67c4 XL |
161 | |
162 | Unfortunately, `Rc<T>` is not safe to share across threads. When `Rc<T>` | |
163 | manages the reference count, it adds to the count for each call to `clone` and | |
164 | subtracts from the count when each clone is dropped. But it doesn’t use any | |
165 | concurrency primitives to make sure that changes to the count can’t be | |
166 | interrupted by another thread. This could lead to wrong counts—subtle bugs that | |
167 | could in turn lead to memory leaks or a value being dropped before we’re done | |
168 | with it. What we need is a type exactly like `Rc<T>` but one that makes changes | |
169 | to the reference count in a thread-safe way. | |
170 | ||
171 | #### Atomic Reference Counting with `Arc<T>` | |
172 | ||
173 | Fortunately, `Arc<T>` *is* a type like `Rc<T>` that is safe to use in | |
174 | concurrent situations. The *a* stands for *atomic*, meaning it’s an *atomically | |
175 | reference counted* type. Atomics are an additional kind of concurrency | |
176 | primitive that we won’t cover in detail here: see the standard library | |
5099ac24 FG |
177 | documentation for [`std::sync::atomic`][atomic]<!-- ignore --> for more |
178 | details. At this point, you just need to know that atomics work like primitive | |
179 | types but are safe to share across threads. | |
3dfed10e | 180 | |
13cf67c4 XL |
181 | You might then wonder why all primitive types aren’t atomic and why standard |
182 | library types aren’t implemented to use `Arc<T>` by default. The reason is that | |
183 | thread safety comes with a performance penalty that you only want to pay when | |
184 | you really need to. If you’re just performing operations on values within a | |
185 | single thread, your code can run faster if it doesn’t have to enforce the | |
186 | guarantees atomics provide. | |
187 | ||
188 | Let’s return to our example: `Arc<T>` and `Rc<T>` have the same API, so we fix | |
189 | our program by changing the `use` line, the call to `new`, and the call to | |
190 | `clone`. The code in Listing 16-15 will finally compile and run: | |
191 | ||
192 | <span class="filename">Filename: src/main.rs</span> | |
193 | ||
194 | ```rust | |
74b04a01 | 195 | {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-15/src/main.rs}} |
13cf67c4 XL |
196 | ``` |
197 | ||
198 | <span class="caption">Listing 16-15: Using an `Arc<T>` to wrap the `Mutex<T>` | |
199 | to be able to share ownership across multiple threads</span> | |
200 | ||
201 | This code will print the following: | |
202 | ||
74b04a01 XL |
203 | <!-- Not extracting output because changes to this output aren't significant; |
204 | the changes are likely to be due to the threads running differently rather than | |
205 | changes in the compiler --> | |
206 | ||
13cf67c4 XL |
207 | ```text |
208 | Result: 10 | |
209 | ``` | |
210 | ||
211 | We did it! We counted from 0 to 10, which may not seem very impressive, but it | |
212 | did teach us a lot about `Mutex<T>` and thread safety. You could also use this | |
213 | program’s structure to do more complicated operations than just incrementing a | |
214 | counter. Using this strategy, you can divide a calculation into independent | |
215 | parts, split those parts across threads, and then use a `Mutex<T>` to have each | |
216 | thread update the final result with its part. | |
217 | ||
218 | ### Similarities Between `RefCell<T>`/`Rc<T>` and `Mutex<T>`/`Arc<T>` | |
219 | ||
220 | You might have noticed that `counter` is immutable but we could get a mutable | |
221 | reference to the value inside it; this means `Mutex<T>` provides interior | |
222 | mutability, as the `Cell` family does. In the same way we used `RefCell<T>` in | |
223 | Chapter 15 to allow us to mutate contents inside an `Rc<T>`, we use `Mutex<T>` | |
224 | to mutate contents inside an `Arc<T>`. | |
225 | ||
226 | Another detail to note is that Rust can’t protect you from all kinds of logic | |
227 | errors when you use `Mutex<T>`. Recall in Chapter 15 that using `Rc<T>` came | |
228 | with the risk of creating reference cycles, where two `Rc<T>` values refer to | |
229 | each other, causing memory leaks. Similarly, `Mutex<T>` comes with the risk of | |
230 | creating *deadlocks*. These occur when an operation needs to lock two resources | |
231 | and two threads have each acquired one of the locks, causing them to wait for | |
232 | each other forever. If you’re interested in deadlocks, try creating a Rust | |
233 | program that has a deadlock; then research deadlock mitigation strategies for | |
234 | mutexes in any language and have a go at implementing them in Rust. The | |
235 | standard library API documentation for `Mutex<T>` and `MutexGuard` offers | |
236 | useful information. | |
237 | ||
238 | We’ll round out this chapter by talking about the `Send` and `Sync` traits and | |
239 | how we can use them with custom types. | |
5099ac24 FG |
240 | |
241 | [atomic]: ../std/sync/atomic/index.html |