2022-01-27 02:44:28 +00:00
|
|
|
|
<!-- DO NOT EDIT THIS FILE.
|
|
|
|
|
|
|
|
|
|
This file is periodically generated from the content in the `/src/`
|
|
|
|
|
directory, so all fixes need to be made in `/src/`.
|
|
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
[TOC]
|
|
|
|
|
|
|
|
|
|
# Fearless Concurrency
|
|
|
|
|
|
|
|
|
|
Handling concurrent programming safely and efficiently is another of Rust’s
|
|
|
|
|
major goals. *Concurrent programming*, where different parts of a program
|
|
|
|
|
execute independently, and *parallel programming*, where different parts of a
|
|
|
|
|
program execute at the same time, are becoming increasingly important as more
|
|
|
|
|
computers take advantage of their multiple processors. Historically,
|
|
|
|
|
programming in these contexts has been difficult and error prone: Rust hopes to
|
|
|
|
|
change that.
|
|
|
|
|
|
|
|
|
|
Initially, the Rust team thought that ensuring memory safety and preventing
|
|
|
|
|
concurrency problems were two separate challenges to be solved with different
|
|
|
|
|
methods. Over time, the team discovered that the ownership and type systems are
|
|
|
|
|
a powerful set of tools to help manage memory safety *and* concurrency
|
|
|
|
|
problems! By leveraging ownership and type checking, many concurrency errors
|
|
|
|
|
are compile-time errors in Rust rather than runtime errors. Therefore, rather
|
|
|
|
|
than making you spend lots of time trying to reproduce the exact circumstances
|
|
|
|
|
under which a runtime concurrency bug occurs, incorrect code will refuse to
|
|
|
|
|
compile and present an error explaining the problem. As a result, you can fix
|
|
|
|
|
your code while you’re working on it rather than potentially after it has been
|
|
|
|
|
shipped to production. We’ve nicknamed this aspect of Rust *fearless*
|
|
|
|
|
*concurrency*. Fearless concurrency allows you to write code that is free of
|
|
|
|
|
subtle bugs and is easy to refactor without introducing new bugs.
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
> Note: For simplicity’s sake, we’ll refer to many of the problems as
|
|
|
|
|
*concurrent* rather than being more precise by saying *concurrent and/or
|
|
|
|
|
parallel*. If this book were about concurrency and/or parallelism, we’d be more
|
|
|
|
|
specific. For this chapter, please mentally substitute *concurrent and/or
|
|
|
|
|
parallel* whenever we use *concurrent*.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Many languages are dogmatic about the solutions they offer for handling
|
|
|
|
|
concurrent problems. For example, Erlang has elegant functionality for
|
|
|
|
|
message-passing concurrency but has only obscure ways to share state between
|
|
|
|
|
threads. Supporting only a subset of possible solutions is a reasonable
|
2022-08-26 20:13:02 +00:00
|
|
|
|
strategy for higher-level languages because a higher-level language promises
|
2022-01-27 02:44:28 +00:00
|
|
|
|
benefits from giving up some control to gain abstractions. However, lower-level
|
|
|
|
|
languages are expected to provide the solution with the best performance in any
|
|
|
|
|
given situation and have fewer abstractions over the hardware. Therefore, Rust
|
|
|
|
|
offers a variety of tools for modeling problems in whatever way is appropriate
|
|
|
|
|
for your situation and requirements.
|
|
|
|
|
|
|
|
|
|
Here are the topics we’ll cover in this chapter:
|
|
|
|
|
|
|
|
|
|
* How to create threads to run multiple pieces of code at the same time
|
|
|
|
|
* *Message-passing* concurrency, where channels send messages between threads
|
|
|
|
|
* *Shared-state* concurrency, where multiple threads have access to some piece
|
2022-08-26 20:13:02 +00:00
|
|
|
|
of data
|
2022-01-27 02:44:28 +00:00
|
|
|
|
* The `Sync` and `Send` traits, which extend Rust’s concurrency guarantees to
|
2022-08-26 20:13:02 +00:00
|
|
|
|
user-defined types as well as types provided by the standard library
|
2022-09-13 16:54:09 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
## Using Threads to Run Code Simultaneously
|
|
|
|
|
|
|
|
|
|
In most current operating systems, an executed program’s code is run in a
|
2022-05-02 00:11:23 +00:00
|
|
|
|
*process*, and the operating system will manage multiple processes at once.
|
|
|
|
|
Within a program, you can also have independent parts that run simultaneously.
|
|
|
|
|
The features that run these independent parts are called *threads*. For
|
|
|
|
|
example, a web server could have multiple threads so that it could respond to
|
|
|
|
|
more than one request at the same time.
|
|
|
|
|
|
|
|
|
|
Splitting the computation in your program into multiple threads to run multiple
|
|
|
|
|
tasks at the same time can improve performance, but it also adds complexity.
|
|
|
|
|
Because threads can run simultaneously, there’s no inherent guarantee about the
|
|
|
|
|
order in which parts of your code on different threads will run. This can lead
|
|
|
|
|
to problems, such as:
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
* Race conditions, where threads are accessing data or resources in an
|
2022-08-26 20:13:02 +00:00
|
|
|
|
inconsistent order
|
2022-05-02 00:11:23 +00:00
|
|
|
|
* Deadlocks, where two threads are waiting for each other, preventing both
|
2022-08-26 20:13:02 +00:00
|
|
|
|
threads from continuing
|
2022-01-27 02:44:28 +00:00
|
|
|
|
* Bugs that happen only in certain situations and are hard to reproduce and fix
|
2022-08-26 20:13:02 +00:00
|
|
|
|
reliably
|
2022-09-13 16:54:09 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
Rust attempts to mitigate the negative effects of using threads, but
|
|
|
|
|
programming in a multithreaded context still takes careful thought and requires
|
|
|
|
|
a code structure that is different from that in programs running in a single
|
|
|
|
|
thread.
|
|
|
|
|
|
2022-05-02 00:11:23 +00:00
|
|
|
|
Programming languages implement threads in a few different ways, and many
|
|
|
|
|
operating systems provide an API the language can call for creating new
|
|
|
|
|
threads. The Rust standard library uses a *1:1* model of thread implementation,
|
|
|
|
|
whereby a program uses one operating system thread per one language thread.
|
|
|
|
|
There are crates that implement other models of threading that make different
|
2022-08-26 20:13:02 +00:00
|
|
|
|
trade-offs to the 1:1 model.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
### Creating a New Thread with spawn
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
To create a new thread, we call the `thread::spawn` function and pass it a
|
|
|
|
|
closure (we talked about closures in Chapter 13) containing the code we want to
|
|
|
|
|
run in the new thread. The example in Listing 16-1 prints some text from a main
|
2022-08-26 20:13:02 +00:00
|
|
|
|
thread and other text from a new thread.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::thread;
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
thread::spawn(|| {
|
|
|
|
|
for i in 1..10 {
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("hi number {i} from the spawned thread!");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for i in 1..5 {
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("hi number {i} from the main thread!");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-1: Creating a new thread to print one thing while the main thread
|
|
|
|
|
prints something else
|
2022-05-02 00:11:23 +00:00
|
|
|
|
|
|
|
|
|
Note that when the main thread of a Rust program completes, all spawned threads
|
|
|
|
|
are shut down, whether or not they have finished running. The output from this
|
2022-01-27 02:44:28 +00:00
|
|
|
|
program might be a little different every time, but it will look similar to the
|
|
|
|
|
following:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
hi number 1 from the main thread!
|
|
|
|
|
hi number 1 from the spawned thread!
|
|
|
|
|
hi number 2 from the main thread!
|
|
|
|
|
hi number 2 from the spawned thread!
|
|
|
|
|
hi number 3 from the main thread!
|
|
|
|
|
hi number 3 from the spawned thread!
|
|
|
|
|
hi number 4 from the main thread!
|
|
|
|
|
hi number 4 from the spawned thread!
|
|
|
|
|
hi number 5 from the spawned thread!
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The calls to `thread::sleep` force a thread to stop its execution for a short
|
|
|
|
|
duration, allowing a different thread to run. The threads will probably take
|
|
|
|
|
turns, but that isn’t guaranteed: it depends on how your operating system
|
|
|
|
|
schedules the threads. In this run, the main thread printed first, even though
|
|
|
|
|
the print statement from the spawned thread appears first in the code. And even
|
|
|
|
|
though we told the spawned thread to print until `i` is 9, it only got to 5
|
|
|
|
|
before the main thread shut down.
|
|
|
|
|
|
|
|
|
|
If you run this code and only see output from the main thread, or don’t see any
|
|
|
|
|
overlap, try increasing the numbers in the ranges to create more opportunities
|
|
|
|
|
for the operating system to switch between the threads.
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
### Waiting for All Threads to Finish Using join Handles
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
The code in Listing 16-1 not only stops the spawned thread prematurely most of
|
2022-05-02 00:11:23 +00:00
|
|
|
|
the time due to the main thread ending, but because there is no guarantee on
|
|
|
|
|
the order in which threads run, we also can’t guarantee that the spawned thread
|
|
|
|
|
will get to run at all!
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
We can fix the problem of the spawned thread not running or of it ending
|
|
|
|
|
prematurely by saving the return value of `thread::spawn` in a variable. The
|
2022-09-13 16:54:09 +00:00
|
|
|
|
return type of `thread::spawn` is `JoinHandle<T>`. A `JoinHandle<T>` is an
|
2022-08-26 20:14:47 +00:00
|
|
|
|
owned value that, when we call the `join` method on it, will wait for its
|
2022-09-13 16:54:09 +00:00
|
|
|
|
thread to finish. Listing 16-2 shows how to use the `JoinHandle<T>` of the
|
2022-08-26 20:14:47 +00:00
|
|
|
|
thread we created in Listing 16-1 and call `join` to make sure the spawned
|
|
|
|
|
thread finishes before `main` exits.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::thread;
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let handle = thread::spawn(|| {
|
|
|
|
|
for i in 1..10 {
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("hi number {i} from the spawned thread!");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for i in 1..5 {
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("hi number {i} from the main thread!");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-2: Saving a `JoinHandle<T>` from `thread::spawn` to guarantee the
|
|
|
|
|
thread is run to completion
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Calling `join` on the handle blocks the thread currently running until the
|
|
|
|
|
thread represented by the handle terminates. *Blocking* a thread means that
|
|
|
|
|
thread is prevented from performing work or exiting. Because we’ve put the call
|
|
|
|
|
to `join` after the main thread’s `for` loop, running Listing 16-2 should
|
|
|
|
|
produce output similar to this:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
hi number 1 from the main thread!
|
|
|
|
|
hi number 2 from the main thread!
|
|
|
|
|
hi number 1 from the spawned thread!
|
|
|
|
|
hi number 3 from the main thread!
|
|
|
|
|
hi number 2 from the spawned thread!
|
|
|
|
|
hi number 4 from the main thread!
|
|
|
|
|
hi number 3 from the spawned thread!
|
|
|
|
|
hi number 4 from the spawned thread!
|
|
|
|
|
hi number 5 from the spawned thread!
|
|
|
|
|
hi number 6 from the spawned thread!
|
|
|
|
|
hi number 7 from the spawned thread!
|
|
|
|
|
hi number 8 from the spawned thread!
|
|
|
|
|
hi number 9 from the spawned thread!
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The two threads continue alternating, but the main thread waits because of the
|
|
|
|
|
call to `handle.join()` and does not end until the spawned thread is finished.
|
|
|
|
|
|
|
|
|
|
But let’s see what happens when we instead move `handle.join()` before the
|
|
|
|
|
`for` loop in `main`, like this:
|
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::thread;
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let handle = thread::spawn(|| {
|
|
|
|
|
for i in 1..10 {
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("hi number {i} from the spawned thread!");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
|
|
|
|
|
for i in 1..5 {
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("hi number {i} from the main thread!");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The main thread will wait for the spawned thread to finish and then run its
|
|
|
|
|
`for` loop, so the output won’t be interleaved anymore, as shown here:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
hi number 1 from the spawned thread!
|
|
|
|
|
hi number 2 from the spawned thread!
|
|
|
|
|
hi number 3 from the spawned thread!
|
|
|
|
|
hi number 4 from the spawned thread!
|
|
|
|
|
hi number 5 from the spawned thread!
|
|
|
|
|
hi number 6 from the spawned thread!
|
|
|
|
|
hi number 7 from the spawned thread!
|
|
|
|
|
hi number 8 from the spawned thread!
|
|
|
|
|
hi number 9 from the spawned thread!
|
|
|
|
|
hi number 1 from the main thread!
|
|
|
|
|
hi number 2 from the main thread!
|
|
|
|
|
hi number 3 from the main thread!
|
|
|
|
|
hi number 4 from the main thread!
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Small details, such as where `join` is called, can affect whether or not your
|
|
|
|
|
threads run at the same time.
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
### Using move Closures with Threads
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
We’ll often use the `move` keyword with closures passed to `thread::spawn`
|
2022-01-27 02:44:28 +00:00
|
|
|
|
because the closure will then take ownership of the values it uses from the
|
|
|
|
|
environment, thus transferring ownership of those values from one thread to
|
2022-08-26 20:13:02 +00:00
|
|
|
|
another. In “Capturing the Environment with Closures” on page XX, we discussed
|
|
|
|
|
`move` in the context of closures. Now we’ll concentrate more on the
|
|
|
|
|
interaction between `move` and `thread::spawn`.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Notice in Listing 16-1 that the closure we pass to `thread::spawn` takes no
|
|
|
|
|
arguments: we’re not using any data from the main thread in the spawned
|
|
|
|
|
thread’s code. To use data from the main thread in the spawned thread, the
|
|
|
|
|
spawned thread’s closure must capture the values it needs. Listing 16-3 shows
|
|
|
|
|
an attempt to create a vector in the main thread and use it in the spawned
|
2022-08-26 20:13:02 +00:00
|
|
|
|
thread. However, this won’t work yet, as you’ll see in a moment.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::thread;
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let v = vec![1, 2, 3];
|
|
|
|
|
|
|
|
|
|
let handle = thread::spawn(|| {
|
|
|
|
|
println!("Here's a vector: {:?}", v);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-3: Attempting to use a vector created by the main thread in another
|
|
|
|
|
thread
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
The closure uses `v`, so it will capture `v` and make it part of the closure’s
|
|
|
|
|
environment. Because `thread::spawn` runs this closure in a new thread, we
|
|
|
|
|
should be able to access `v` inside that new thread. But when we compile this
|
|
|
|
|
example, we get the following error:
|
|
|
|
|
|
|
|
|
|
```
|
2022-08-26 20:14:47 +00:00
|
|
|
|
error[E0373]: closure may outlive the current function, but it borrows `v`,
|
|
|
|
|
which is owned by the current function
|
2022-01-27 02:44:28 +00:00
|
|
|
|
--> src/main.rs:6:32
|
|
|
|
|
|
|
|
|
|
|
6 | let handle = thread::spawn(|| {
|
|
|
|
|
| ^^ may outlive borrowed value `v`
|
|
|
|
|
7 | println!("Here's a vector: {:?}", v);
|
|
|
|
|
| - `v` is borrowed here
|
|
|
|
|
|
|
|
|
|
|
note: function requires argument type to outlive `'static`
|
|
|
|
|
--> src/main.rs:6:18
|
|
|
|
|
|
|
|
|
|
|
6 | let handle = thread::spawn(|| {
|
|
|
|
|
| __________________^
|
|
|
|
|
7 | | println!("Here's a vector: {:?}", v);
|
|
|
|
|
8 | | });
|
|
|
|
|
| |______^
|
2022-08-26 20:14:47 +00:00
|
|
|
|
help: to force the closure to take ownership of `v` (and any other referenced
|
|
|
|
|
variables), use the `move` keyword
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
|
6 | let handle = thread::spawn(move || {
|
|
|
|
|
| ++++
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Rust *infers* how to capture `v`, and because `println!` only needs a reference
|
|
|
|
|
to `v`, the closure tries to borrow `v`. However, there’s a problem: Rust can’t
|
2022-08-26 20:13:02 +00:00
|
|
|
|
tell how long the spawned thread will run, so it doesn’t know whether the
|
|
|
|
|
reference to `v` will always be valid.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Listing 16-4 provides a scenario that’s more likely to have a reference to `v`
|
2022-08-26 20:13:02 +00:00
|
|
|
|
that won’t be valid.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::thread;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let v = vec![1, 2, 3];
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
let handle = thread::spawn(|| {
|
|
|
|
|
println!("Here's a vector: {:?}", v);
|
|
|
|
|
});
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
drop(v); // oh no!
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-4: A thread with a closure that attempts to capture a reference to
|
|
|
|
|
`v` from a main thread that drops `v`
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
If Rust allowed us to run this code, there’s a possibility that the spawned
|
|
|
|
|
thread would be immediately put in the background without running at all. The
|
|
|
|
|
spawned thread has a reference to `v` inside, but the main thread immediately
|
|
|
|
|
drops `v`, using the `drop` function we discussed in Chapter 15. Then, when the
|
2022-01-27 02:44:28 +00:00
|
|
|
|
spawned thread starts to execute, `v` is no longer valid, so a reference to it
|
|
|
|
|
is also invalid. Oh no!
|
|
|
|
|
|
|
|
|
|
To fix the compiler error in Listing 16-3, we can use the error message’s
|
|
|
|
|
advice:
|
|
|
|
|
|
|
|
|
|
```
|
2022-08-26 20:14:47 +00:00
|
|
|
|
help: to force the closure to take ownership of `v` (and any other referenced
|
|
|
|
|
variables), use the `move` keyword
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
|
6 | let handle = thread::spawn(move || {
|
|
|
|
|
| ++++
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
By adding the `move` keyword before the closure, we force the closure to take
|
|
|
|
|
ownership of the values it’s using rather than allowing Rust to infer that it
|
|
|
|
|
should borrow the values. The modification to Listing 16-3 shown in Listing
|
2022-08-26 20:13:02 +00:00
|
|
|
|
16-5 will compile and run as we intend.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::thread;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let v = vec![1, 2, 3];
|
|
|
|
|
|
|
|
|
|
let handle = thread::spawn(move || {
|
|
|
|
|
println!("Here's a vector: {:?}", v);
|
|
|
|
|
});
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-5: Using the `move` keyword to force a closure to take ownership of
|
|
|
|
|
the values it uses
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-05-02 00:11:23 +00:00
|
|
|
|
We might be tempted to try the same thing to fix the code in Listing 16-4 where
|
|
|
|
|
the main thread called `drop` by using a `move` closure. However, this fix will
|
|
|
|
|
not work because what Listing 16-4 is trying to do is disallowed for a
|
|
|
|
|
different reason. If we added `move` to the closure, we would move `v` into the
|
|
|
|
|
closure’s environment, and we could no longer call `drop` on it in the main
|
|
|
|
|
thread. We would get this compiler error instead:
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
error[E0382]: use of moved value: `v`
|
|
|
|
|
--> src/main.rs:10:10
|
|
|
|
|
|
|
|
|
|
|
4 | let v = vec![1, 2, 3];
|
2022-08-26 20:13:02 +00:00
|
|
|
|
| - move occurs because `v` has type `Vec<i32>`, which does not
|
|
|
|
|
implement the `Copy` trait
|
2022-01-27 02:44:28 +00:00
|
|
|
|
5 |
|
|
|
|
|
6 | let handle = thread::spawn(move || {
|
|
|
|
|
| ------- value moved into closure here
|
|
|
|
|
7 | println!("Here's a vector: {:?}", v);
|
2022-08-26 20:13:02 +00:00
|
|
|
|
| - variable moved due to use in
|
|
|
|
|
closure
|
2022-01-27 02:44:28 +00:00
|
|
|
|
...
|
|
|
|
|
10 | drop(v); // oh no!
|
|
|
|
|
| ^ value used here after move
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Rust’s ownership rules have saved us again! We got an error from the code in
|
|
|
|
|
Listing 16-3 because Rust was being conservative and only borrowing `v` for the
|
|
|
|
|
thread, which meant the main thread could theoretically invalidate the spawned
|
|
|
|
|
thread’s reference. By telling Rust to move ownership of `v` to the spawned
|
|
|
|
|
thread, we’re guaranteeing Rust that the main thread won’t use `v` anymore. If
|
|
|
|
|
we change Listing 16-4 in the same way, we’re then violating the ownership
|
|
|
|
|
rules when we try to use `v` in the main thread. The `move` keyword overrides
|
|
|
|
|
Rust’s conservative default of borrowing; it doesn’t let us violate the
|
|
|
|
|
ownership rules.
|
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
Now that we’ve covered what threads are and the methods supplied by the thread
|
|
|
|
|
API, let’s look at some situations in which we can use threads.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
## Using Message Passing to Transfer Data Between Threads
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
One increasingly popular approach to ensuring safe concurrency is *message
|
|
|
|
|
passing*, where threads or actors communicate by sending each other messages
|
2022-08-26 20:13:02 +00:00
|
|
|
|
containing data. Here’s the idea in a slogan from the Go language documentation
|
|
|
|
|
at *https://golang.org/doc/effective_go.html#concurrency*: “Do not communicate
|
|
|
|
|
by sharing memory; instead, share memory by communicating.”
|
2022-05-02 00:11:23 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
To accomplish message-sending concurrency, Rust’s standard library provides an
|
2022-05-02 00:11:23 +00:00
|
|
|
|
implementation of *channels*. A channel is a general programming concept by
|
|
|
|
|
which data is sent from one thread to another.
|
|
|
|
|
|
|
|
|
|
You can imagine a channel in programming as being like a directional channel of
|
|
|
|
|
water, such as a stream or a river. If you put something like a rubber duck
|
|
|
|
|
into a river, it will travel downstream to the end of the waterway.
|
|
|
|
|
|
|
|
|
|
A channel has two halves: a transmitter and a receiver. The transmitter half is
|
2022-08-26 20:13:02 +00:00
|
|
|
|
the upstream location where you put the rubber duck into the river, and the
|
2022-05-02 00:11:23 +00:00
|
|
|
|
receiver half is where the rubber duck ends up downstream. One part of your
|
|
|
|
|
code calls methods on the transmitter with the data you want to send, and
|
|
|
|
|
another part checks the receiving end for arriving messages. A channel is said
|
|
|
|
|
to be *closed* if either the transmitter or receiver half is dropped.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Here, we’ll work up to a program that has one thread to generate values and
|
|
|
|
|
send them down a channel, and another thread that will receive the values and
|
|
|
|
|
print them out. We’ll be sending simple values between threads using a channel
|
|
|
|
|
to illustrate the feature. Once you’re familiar with the technique, you could
|
2022-08-26 20:13:02 +00:00
|
|
|
|
use channels for any threads that need to communicate with each other, such as
|
|
|
|
|
a chat system or a system where many threads perform parts of a calculation and
|
|
|
|
|
send the parts to one thread that aggregates the results.
|
2022-05-02 00:11:23 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
First, in Listing 16-6, we’ll create a channel but not do anything with it.
|
|
|
|
|
Note that this won’t compile yet because Rust can’t tell what type of values we
|
|
|
|
|
want to send over the channel.
|
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::mpsc;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-6: Creating a channel and assigning the two halves to `tx` and `rx`
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
We create a new channel using the `mpsc::channel` function; `mpsc` stands for
|
|
|
|
|
*multiple producer, single consumer*. In short, the way Rust’s standard library
|
|
|
|
|
implements channels means a channel can have multiple *sending* ends that
|
|
|
|
|
produce values but only one *receiving* end that consumes those values. Imagine
|
|
|
|
|
multiple streams flowing together into one big river: everything sent down any
|
|
|
|
|
of the streams will end up in one river at the end. We’ll start with a single
|
|
|
|
|
producer for now, but we’ll add multiple producers when we get this example
|
|
|
|
|
working.
|
|
|
|
|
|
|
|
|
|
The `mpsc::channel` function returns a tuple, the first element of which is the
|
2022-08-26 20:13:02 +00:00
|
|
|
|
sending end—the transmitter—and the second element of which is the receiving
|
|
|
|
|
end—the receiver. The abbreviations `tx` and `rx` are traditionally used in
|
|
|
|
|
many fields for *transmitter* and *receiver*, respectively, so we name our
|
|
|
|
|
variables as such to indicate each end. We’re using a `let` statement with a
|
|
|
|
|
pattern that destructures the tuples; we’ll discuss the use of patterns in
|
|
|
|
|
`let` statements and destructuring in Chapter 18. For now, know that using a
|
|
|
|
|
`let` statement in this way is a convenient approach to extract the pieces of
|
|
|
|
|
the tuple returned by `mpsc::channel`.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Let’s move the transmitting end into a spawned thread and have it send one
|
|
|
|
|
string so the spawned thread is communicating with the main thread, as shown in
|
|
|
|
|
Listing 16-7. This is like putting a rubber duck in the river upstream or
|
|
|
|
|
sending a chat message from one thread to another.
|
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::mpsc;
|
|
|
|
|
use std::thread;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::spawn(move || {
|
|
|
|
|
let val = String::from("hi");
|
|
|
|
|
tx.send(val).unwrap();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-7: Moving `tx` to a spawned thread and sending `"hi"`
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Again, we’re using `thread::spawn` to create a new thread and then using `move`
|
|
|
|
|
to move `tx` into the closure so the spawned thread owns `tx`. The spawned
|
2022-05-02 00:11:23 +00:00
|
|
|
|
thread needs to own the transmitter to be able to send messages through the
|
2022-08-26 20:13:02 +00:00
|
|
|
|
channel.
|
|
|
|
|
|
|
|
|
|
The transmitter has a `send` method that takes the value we want to send. The
|
|
|
|
|
`send` method returns a `Result<T, E>` type, so if the receiver has already
|
|
|
|
|
been dropped and there’s nowhere to send a value, the send operation will
|
|
|
|
|
return an error. In this example, we’re calling `unwrap` to panic in case of an
|
|
|
|
|
error. But in a real application, we would handle it properly: return to
|
2022-01-27 02:44:28 +00:00
|
|
|
|
Chapter 9 to review strategies for proper error handling.
|
|
|
|
|
|
2022-05-02 00:11:23 +00:00
|
|
|
|
In Listing 16-8, we’ll get the value from the receiver in the main thread. This
|
|
|
|
|
is like retrieving the rubber duck from the water at the end of the river or
|
|
|
|
|
receiving a chat message.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::mpsc;
|
|
|
|
|
use std::thread;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::spawn(move || {
|
|
|
|
|
let val = String::from("hi");
|
|
|
|
|
tx.send(val).unwrap();
|
|
|
|
|
});
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
let received = rx.recv().unwrap();
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("Got: {received}");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-8: Receiving the value `"hi"` in the main thread and printing it
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-05-02 00:11:23 +00:00
|
|
|
|
The receiver has two useful methods: `recv` and `try_recv`. We’re using `recv`,
|
|
|
|
|
short for *receive*, which will block the main thread’s execution and wait
|
|
|
|
|
until a value is sent down the channel. Once a value is sent, `recv` will
|
|
|
|
|
return it in a `Result<T, E>`. When the transmitter closes, `recv` will return
|
|
|
|
|
an error to signal that no more values will be coming.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
The `try_recv` method doesn’t block, but will instead return a `Result<T, E>`
|
|
|
|
|
immediately: an `Ok` value holding a message if one is available and an `Err`
|
|
|
|
|
value if there aren’t any messages this time. Using `try_recv` is useful if
|
|
|
|
|
this thread has other work to do while waiting for messages: we could write a
|
|
|
|
|
loop that calls `try_recv` every so often, handles a message if one is
|
|
|
|
|
available, and otherwise does other work for a little while until checking
|
|
|
|
|
again.
|
|
|
|
|
|
|
|
|
|
We’ve used `recv` in this example for simplicity; we don’t have any other work
|
|
|
|
|
for the main thread to do other than wait for messages, so blocking the main
|
|
|
|
|
thread is appropriate.
|
|
|
|
|
|
|
|
|
|
When we run the code in Listing 16-8, we’ll see the value printed from the main
|
|
|
|
|
thread:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Got: hi
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Perfect!
|
|
|
|
|
|
|
|
|
|
### Channels and Ownership Transference
|
|
|
|
|
|
|
|
|
|
The ownership rules play a vital role in message sending because they help you
|
|
|
|
|
write safe, concurrent code. Preventing errors in concurrent programming is the
|
|
|
|
|
advantage of thinking about ownership throughout your Rust programs. Let’s do
|
|
|
|
|
an experiment to show how channels and ownership work together to prevent
|
|
|
|
|
problems: we’ll try to use a `val` value in the spawned thread *after* we’ve
|
|
|
|
|
sent it down the channel. Try compiling the code in Listing 16-9 to see why
|
2022-08-26 20:13:02 +00:00
|
|
|
|
this code isn’t allowed.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::mpsc;
|
|
|
|
|
use std::thread;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::spawn(move || {
|
|
|
|
|
let val = String::from("hi");
|
|
|
|
|
tx.send(val).unwrap();
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("val is {val}");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
});
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
let received = rx.recv().unwrap();
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("Got: {received}");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-9: Attempting to use `val` after we’ve sent it down the channel
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Here, we try to print `val` after we’ve sent it down the channel via `tx.send`.
|
|
|
|
|
Allowing this would be a bad idea: once the value has been sent to another
|
|
|
|
|
thread, that thread could modify or drop it before we try to use the value
|
|
|
|
|
again. Potentially, the other thread’s modifications could cause errors or
|
|
|
|
|
unexpected results due to inconsistent or nonexistent data. However, Rust gives
|
|
|
|
|
us an error if we try to compile the code in Listing 16-9:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
error[E0382]: borrow of moved value: `val`
|
|
|
|
|
--> src/main.rs:10:31
|
|
|
|
|
|
|
|
|
|
|
8 | let val = String::from("hi");
|
2022-08-26 20:14:47 +00:00
|
|
|
|
| --- move occurs because `val` has type `String`, which does
|
|
|
|
|
not implement the `Copy` trait
|
2022-01-27 02:44:28 +00:00
|
|
|
|
9 | tx.send(val).unwrap();
|
|
|
|
|
| --- value moved here
|
2022-08-26 20:14:47 +00:00
|
|
|
|
10 | println!("val is {val}");
|
|
|
|
|
| ^^^ value borrowed here after move
|
2022-01-27 02:44:28 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
Our concurrency mistake has caused a compile-time error. The `send` function
|
|
|
|
|
takes ownership of its parameter, and when the value is moved the receiver
|
2022-01-27 02:44:28 +00:00
|
|
|
|
takes ownership of it. This stops us from accidentally using the value again
|
|
|
|
|
after sending it; the ownership system checks that everything is okay.
|
|
|
|
|
|
|
|
|
|
### Sending Multiple Values and Seeing the Receiver Waiting
|
|
|
|
|
|
|
|
|
|
The code in Listing 16-8 compiled and ran, but it didn’t clearly show us that
|
|
|
|
|
two separate threads were talking to each other over the channel. In Listing
|
|
|
|
|
16-10 we’ve made some modifications that will prove the code in Listing 16-8 is
|
|
|
|
|
running concurrently: the spawned thread will now send multiple messages and
|
|
|
|
|
pause for a second between each message.
|
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::mpsc;
|
|
|
|
|
use std::thread;
|
|
|
|
|
use std::time::Duration;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
thread::spawn(move || {
|
|
|
|
|
let vals = vec![
|
|
|
|
|
String::from("hi"),
|
|
|
|
|
String::from("from"),
|
|
|
|
|
String::from("the"),
|
|
|
|
|
String::from("thread"),
|
|
|
|
|
];
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
for val in vals {
|
|
|
|
|
tx.send(val).unwrap();
|
|
|
|
|
thread::sleep(Duration::from_secs(1));
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
for received in rx {
|
2022-08-26 20:14:47 +00:00
|
|
|
|
println!("Got: {received}");
|
2022-01-27 02:44:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-10: Sending multiple messages and pausing between each one
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
This time, the spawned thread has a vector of strings that we want to send to
|
|
|
|
|
the main thread. We iterate over them, sending each individually, and pause
|
|
|
|
|
between each by calling the `thread::sleep` function with a `Duration` value of
|
2022-08-26 20:13:02 +00:00
|
|
|
|
one second.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
In the main thread, we’re not calling the `recv` function explicitly anymore:
|
|
|
|
|
instead, we’re treating `rx` as an iterator. For each value received, we’re
|
|
|
|
|
printing it. When the channel is closed, iteration will end.
|
|
|
|
|
|
|
|
|
|
When running the code in Listing 16-10, you should see the following output
|
2022-08-26 20:13:02 +00:00
|
|
|
|
with a one-second pause in between each line:
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Got: hi
|
|
|
|
|
Got: from
|
|
|
|
|
Got: the
|
|
|
|
|
Got: thread
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Because we don’t have any code that pauses or delays in the `for` loop in the
|
|
|
|
|
main thread, we can tell that the main thread is waiting to receive values from
|
|
|
|
|
the spawned thread.
|
|
|
|
|
|
|
|
|
|
### Creating Multiple Producers by Cloning the Transmitter
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Earlier we mentioned that `mpsc` was an acronym for *multiple producer, single
|
|
|
|
|
consumer*. Let’s put `mpsc` to use and expand the code in Listing 16-10 to
|
|
|
|
|
create multiple threads that all send values to the same receiver. We can do so
|
|
|
|
|
by cloning the transmitter, as shown in Listing 16-11.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
2022-08-26 20:14:47 +00:00
|
|
|
|
--snip--
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
let (tx, rx) = mpsc::channel();
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
let tx1 = tx.clone();
|
|
|
|
|
thread::spawn(move || {
|
|
|
|
|
let vals = vec![
|
|
|
|
|
String::from("hi"),
|
|
|
|
|
String::from("from"),
|
|
|
|
|
String::from("the"),
|
|
|
|
|
String::from("thread"),
|
|
|
|
|
];
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
for val in vals {
|
|
|
|
|
tx1.send(val).unwrap();
|
|
|
|
|
thread::sleep(Duration::from_secs(1));
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
thread::spawn(move || {
|
|
|
|
|
let vals = vec![
|
|
|
|
|
String::from("more"),
|
|
|
|
|
String::from("messages"),
|
|
|
|
|
String::from("for"),
|
|
|
|
|
String::from("you"),
|
|
|
|
|
];
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
for val in vals {
|
|
|
|
|
tx.send(val).unwrap();
|
|
|
|
|
thread::sleep(Duration::from_secs(1));
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
for received in rx {
|
|
|
|
|
println!("Got: {received}");
|
|
|
|
|
}
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-08-26 20:14:47 +00:00
|
|
|
|
--snip--
|
2022-08-26 20:13:02 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-11: Sending multiple messages from multiple producers
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
This time, before we create the first spawned thread, we call `clone` on the
|
2022-05-02 00:11:23 +00:00
|
|
|
|
transmitter. This will give us a new transmitter we can pass to the first
|
|
|
|
|
spawned thread. We pass the original transmitter to a second spawned thread.
|
|
|
|
|
This gives us two threads, each sending different messages to the one receiver.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
When you run the code, your output should look something like this:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Got: hi
|
|
|
|
|
Got: more
|
|
|
|
|
Got: from
|
|
|
|
|
Got: messages
|
|
|
|
|
Got: for
|
|
|
|
|
Got: the
|
|
|
|
|
Got: thread
|
|
|
|
|
Got: you
|
|
|
|
|
```
|
|
|
|
|
|
2022-05-01 15:32:24 +00:00
|
|
|
|
You might see the values in another order, depending on your system. This is
|
2022-01-27 02:44:28 +00:00
|
|
|
|
what makes concurrency interesting as well as difficult. If you experiment with
|
|
|
|
|
`thread::sleep`, giving it various values in the different threads, each run
|
|
|
|
|
will be more nondeterministic and create different output each time.
|
|
|
|
|
|
|
|
|
|
Now that we’ve looked at how channels work, let’s look at a different method of
|
|
|
|
|
concurrency.
|
|
|
|
|
|
|
|
|
|
## Shared-State Concurrency
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
Message passing is a fine way to handle concurrency, but it’s not the only way.
|
|
|
|
|
Another method would be for multiple threads to access the same shared data.
|
|
|
|
|
Consider this part of the slogan from the Go language documentation again: “Do
|
|
|
|
|
not communicate by sharing memory.”
|
2022-06-06 00:29:10 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
What would communicating by sharing memory look like? In addition, why would
|
2022-05-02 00:11:23 +00:00
|
|
|
|
message-passing enthusiasts caution not to use memory sharing?
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
In a way, channels in any programming language are similar to single ownership
|
2022-01-27 02:44:28 +00:00
|
|
|
|
because once you transfer a value down a channel, you should no longer use that
|
2022-08-26 20:14:47 +00:00
|
|
|
|
value. Shared-memory concurrency is like multiple ownership: multiple threads
|
2022-01-27 02:44:28 +00:00
|
|
|
|
can access the same memory location at the same time. As you saw in Chapter 15,
|
|
|
|
|
where smart pointers made multiple ownership possible, multiple ownership can
|
|
|
|
|
add complexity because these different owners need managing. Rust’s type system
|
|
|
|
|
and ownership rules greatly assist in getting this management correct. For an
|
|
|
|
|
example, let’s look at mutexes, one of the more common concurrency primitives
|
|
|
|
|
for shared memory.
|
|
|
|
|
|
|
|
|
|
### Using Mutexes to Allow Access to Data from One Thread at a Time
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
*Mutex* is an abbreviation for *mutual exclusion*, as in a mutex allows only
|
2022-01-27 02:44:28 +00:00
|
|
|
|
one thread to access some data at any given time. To access the data in a
|
|
|
|
|
mutex, a thread must first signal that it wants access by asking to acquire the
|
|
|
|
|
mutex’s *lock*. The lock is a data structure that is part of the mutex that
|
|
|
|
|
keeps track of who currently has exclusive access to the data. Therefore, the
|
|
|
|
|
mutex is described as *guarding* the data it holds via the locking system.
|
|
|
|
|
|
|
|
|
|
Mutexes have a reputation for being difficult to use because you have to
|
|
|
|
|
remember two rules:
|
|
|
|
|
|
2022-09-13 17:38:46 +00:00
|
|
|
|
1. You must attempt to acquire the lock before using the data.
|
|
|
|
|
1. When you’re done with the data that the mutex guards, you must unlock the
|
|
|
|
|
data so other threads can acquire the lock.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-09-13 17:38:46 +00:00
|
|
|
|
For a real-world metaphor for a mutex, imagine a panel discussion at a
|
2022-01-27 02:44:28 +00:00
|
|
|
|
conference with only one microphone. Before a panelist can speak, they have to
|
|
|
|
|
ask or signal that they want to use the microphone. When they get the
|
|
|
|
|
microphone, they can talk for as long as they want to and then hand the
|
|
|
|
|
microphone to the next panelist who requests to speak. If a panelist forgets to
|
|
|
|
|
hand the microphone off when they’re finished with it, no one else is able to
|
|
|
|
|
speak. If management of the shared microphone goes wrong, the panel won’t work
|
|
|
|
|
as planned!
|
|
|
|
|
|
|
|
|
|
Management of mutexes can be incredibly tricky to get right, which is why so
|
|
|
|
|
many people are enthusiastic about channels. However, thanks to Rust’s type
|
|
|
|
|
system and ownership rules, you can’t get locking and unlocking wrong.
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
#### The API of Mutex<T>
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
As an example of how to use a mutex, let’s start by using a mutex in a
|
2022-08-26 20:13:02 +00:00
|
|
|
|
single-threaded context, as shown in Listing 16-12.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::Mutex;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
2022-08-26 20:13:02 +00:00
|
|
|
|
1 let m = Mutex::new(5);
|
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
{
|
2022-08-26 20:13:02 +00:00
|
|
|
|
2 let mut num = m.lock().unwrap();
|
2022-08-26 20:14:47 +00:00
|
|
|
|
3 *num = 6;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
4 }
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
5 println!("m = {:?}", m);
|
2022-01-27 02:44:28 +00:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-12: Exploring the API of `Mutex<T>` in a single-threaded context for
|
|
|
|
|
simplicity
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
As with many types, we create a `Mutex<T>` using the associated function `new`
|
|
|
|
|
[1]. To access the data inside the mutex, we use the `lock` method to acquire
|
|
|
|
|
the lock [2]. This call will block the current thread so it can’t do any work
|
|
|
|
|
until it’s our turn to have the lock.
|
|
|
|
|
|
|
|
|
|
The call to `lock` would fail if another thread holding the lock panicked. In
|
|
|
|
|
that case, no one would ever be able to get the lock, so we’ve chosen to
|
|
|
|
|
`unwrap` and have this thread panic if we’re in that situation.
|
|
|
|
|
|
|
|
|
|
After we’ve acquired the lock, we can treat the return value, named `num` in
|
2022-05-02 00:16:08 +00:00
|
|
|
|
this case, as a mutable reference to the data inside. The type system ensures
|
|
|
|
|
that we acquire a lock before using the value in `m`. The type of `m` is
|
|
|
|
|
`Mutex<i32>`, not `i32`, so we *must* call `lock` to be able to use the `i32`
|
|
|
|
|
value. We can’t forget; the type system won’t let us access the inner `i32`
|
|
|
|
|
otherwise.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
As you might suspect, `Mutex<T>` is a smart pointer. More accurately, the call
|
|
|
|
|
to `lock` *returns* a smart pointer called `MutexGuard`, wrapped in a
|
|
|
|
|
`LockResult` that we handled with the call to `unwrap`. The `MutexGuard` smart
|
|
|
|
|
pointer implements `Deref` to point at our inner data; the smart pointer also
|
|
|
|
|
has a `Drop` implementation that releases the lock automatically when a
|
|
|
|
|
`MutexGuard` goes out of scope, which happens at the end of the inner scope
|
|
|
|
|
[4]. As a result, we don’t risk forgetting to release the lock and blocking the
|
2022-08-26 20:13:02 +00:00
|
|
|
|
mutex from being used by other threads because the lock release happens
|
2022-01-27 02:44:28 +00:00
|
|
|
|
automatically.
|
|
|
|
|
|
|
|
|
|
After dropping the lock, we can print the mutex value and see that we were able
|
2022-08-26 20:13:02 +00:00
|
|
|
|
to change the inner `i32` to `6` [5].
|
|
|
|
|
|
|
|
|
|
#### Sharing a Mutex<T> Between Multiple Threads
|
|
|
|
|
|
|
|
|
|
Now let’s try to share a value between multiple threads using `Mutex<T>`. We’ll
|
|
|
|
|
spin up 10 threads and have them each increment a counter value by 1, so the
|
|
|
|
|
counter goes from 0 to 10. The example in Listing 16-13 will have a compiler
|
|
|
|
|
error, and we’ll use that error to learn more about using `Mutex<T>` and how
|
|
|
|
|
Rust helps us use it correctly.
|
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::Mutex;
|
|
|
|
|
use std::thread;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
1 let counter = Mutex::new(0);
|
|
|
|
|
let mut handles = vec![];
|
|
|
|
|
|
|
|
|
|
2 for _ in 0..10 {
|
|
|
|
|
3 let handle = thread::spawn(move || {
|
|
|
|
|
4 let mut num = counter.lock().unwrap();
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
5 *num += 1;
|
|
|
|
|
});
|
|
|
|
|
6 handles.push(handle);
|
2022-01-27 02:44:28 +00:00
|
|
|
|
}
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
for handle in handles {
|
2022-08-26 20:13:02 +00:00
|
|
|
|
7 handle.join().unwrap();
|
2022-01-27 02:44:28 +00:00
|
|
|
|
}
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
|
|
|
|
8 println!("Result: {}", *counter.lock().unwrap());
|
2022-01-27 02:44:28 +00:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-13: Ten threads, each incrementing a counter guarded by a `Mutex<T>`
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
We create a `counter` variable to hold an `i32` inside a `Mutex<T>` [1], as we
|
|
|
|
|
did in Listing 16-12. Next, we create 10 threads by iterating over a range of
|
2022-05-01 15:32:24 +00:00
|
|
|
|
numbers [2]. We use `thread::spawn` and give all the threads the same closure:
|
2022-01-27 02:44:28 +00:00
|
|
|
|
one that moves the counter into the thread [3], acquires a lock on the
|
|
|
|
|
`Mutex<T>` by calling the `lock` method [4], and then adds 1 to the value in
|
|
|
|
|
the mutex [5]. When a thread finishes running its closure, `num` will go out of
|
|
|
|
|
scope and release the lock so another thread can acquire it.
|
|
|
|
|
|
|
|
|
|
In the main thread, we collect all the join handles [6]. Then, as we did in
|
|
|
|
|
Listing 16-2, we call `join` on each handle to make sure all the threads finish
|
|
|
|
|
[7]. At that point, the main thread will acquire the lock and print the result
|
|
|
|
|
of this program [8].
|
|
|
|
|
|
|
|
|
|
We hinted that this example wouldn’t compile. Now let’s find out why!
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
error[E0382]: use of moved value: `counter`
|
|
|
|
|
--> src/main.rs:9:36
|
|
|
|
|
|
|
|
|
|
|
5 | let counter = Mutex::new(0);
|
2022-08-26 20:13:02 +00:00
|
|
|
|
| ------- move occurs because `counter` has type `Mutex<i32>`, which
|
|
|
|
|
does not implement the `Copy` trait
|
2022-01-27 02:44:28 +00:00
|
|
|
|
...
|
|
|
|
|
9 | let handle = thread::spawn(move || {
|
2022-08-26 20:13:02 +00:00
|
|
|
|
| ^^^^^^^ value moved into closure here,
|
|
|
|
|
in previous iteration of loop
|
2022-01-27 02:44:28 +00:00
|
|
|
|
10 | let mut num = counter.lock().unwrap();
|
|
|
|
|
| ------- use occurs due to use in closure
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The error message states that the `counter` value was moved in the previous
|
2022-08-26 20:13:02 +00:00
|
|
|
|
iteration of the loop. Rust is telling us that we can’t move the ownership of
|
2022-08-26 20:14:47 +00:00
|
|
|
|
lock `counter` into multiple threads. Let’s fix the compiler error with the
|
2022-01-27 02:44:28 +00:00
|
|
|
|
multiple-ownership method we discussed in Chapter 15.
|
|
|
|
|
|
|
|
|
|
#### Multiple Ownership with Multiple Threads
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
In Chapter 15, we gave a value to multiple owners by using the smart pointer
|
2022-01-27 02:44:28 +00:00
|
|
|
|
`Rc<T>` to create a reference counted value. Let’s do the same here and see
|
|
|
|
|
what happens. We’ll wrap the `Mutex<T>` in `Rc<T>` in Listing 16-14 and clone
|
|
|
|
|
the `Rc<T>` before moving ownership to the thread.
|
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
use std::sync::Mutex;
|
|
|
|
|
use std::thread;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let counter = Rc::new(Mutex::new(0));
|
|
|
|
|
let mut handles = vec![];
|
|
|
|
|
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
|
let counter = Rc::clone(&counter);
|
|
|
|
|
let handle = thread::spawn(move || {
|
|
|
|
|
let mut num = counter.lock().unwrap();
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
*num += 1;
|
|
|
|
|
});
|
|
|
|
|
handles.push(handle);
|
|
|
|
|
}
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
for handle in handles {
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
}
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
println!("Result: {}", *counter.lock().unwrap());
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-14: Attempting to use `Rc<T>` to allow multiple threads to own the
|
|
|
|
|
`Mutex<T>`
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
|
|
|
|
Once again, we compile and get… different errors! The compiler is teaching us a
|
|
|
|
|
lot.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
```
|
|
|
|
|
error[E0277]: `Rc<Mutex<i32>>` cannot be sent between threads safely 1
|
2022-01-27 02:44:28 +00:00
|
|
|
|
--> src/main.rs:11:22
|
|
|
|
|
|
|
|
|
|
|
11 | let handle = thread::spawn(move || {
|
|
|
|
|
| ______________________^^^^^^^^^^^^^_-
|
|
|
|
|
| | |
|
2022-08-26 20:13:02 +00:00
|
|
|
|
| | `Rc<Mutex<i32>>` cannot be sent between threads
|
|
|
|
|
safely
|
2022-01-27 02:44:28 +00:00
|
|
|
|
12 | | let mut num = counter.lock().unwrap();
|
|
|
|
|
13 | |
|
|
|
|
|
14 | | *num += 1;
|
|
|
|
|
15 | | });
|
|
|
|
|
| |_________- within this `[closure@src/main.rs:11:36: 15:10]`
|
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
= help: within `[closure@src/main.rs:11:36: 15:10]`, the trait `Send` is not
|
|
|
|
|
implemented for `Rc<Mutex<i32>>` 2
|
|
|
|
|
= note: required because it appears within the type
|
|
|
|
|
`[closure@src/main.rs:11:36: 15:10]`
|
2022-01-27 02:44:28 +00:00
|
|
|
|
note: required by a bound in `spawn`
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Wow, that error message is very wordy! Here’s the important part to focus on:
|
2022-08-26 20:13:02 +00:00
|
|
|
|
``Rc<Mutex<i32>>` cannot be sent between threads safely` [1]. The compiler is
|
|
|
|
|
also telling us the reason why: `the trait `Send` is not implemented for
|
|
|
|
|
`Rc<Mutex<i32>>`` [2]. We’ll talk about `Send` in the next section: it’s one of
|
|
|
|
|
the traits that ensures the types we use with threads are meant for use in
|
2022-01-27 02:44:28 +00:00
|
|
|
|
concurrent situations.
|
|
|
|
|
|
|
|
|
|
Unfortunately, `Rc<T>` is not safe to share across threads. When `Rc<T>`
|
|
|
|
|
manages the reference count, it adds to the count for each call to `clone` and
|
|
|
|
|
subtracts from the count when each clone is dropped. But it doesn’t use any
|
|
|
|
|
concurrency primitives to make sure that changes to the count can’t be
|
|
|
|
|
interrupted by another thread. This could lead to wrong counts—subtle bugs that
|
|
|
|
|
could in turn lead to memory leaks or a value being dropped before we’re done
|
|
|
|
|
with it. What we need is a type exactly like `Rc<T>` but one that makes changes
|
|
|
|
|
to the reference count in a thread-safe way.
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
#### Atomic Reference Counting with Arc<T>
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Fortunately, `Arc<T>` *is* a type like `Rc<T>` that is safe to use in
|
2022-09-13 16:54:09 +00:00
|
|
|
|
concurrent situations. The *a* stands for *atomic*, meaning it’s an *atomically
|
|
|
|
|
reference counted* type. Atomics are an additional kind of concurrency
|
|
|
|
|
primitive that we won’t cover in detail here: see the standard library
|
|
|
|
|
documentation for `std::sync::atomic` for more details. At this point, you just
|
|
|
|
|
need to know that atomics work like primitive types but are safe to share
|
|
|
|
|
across threads.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
You might then wonder why all primitive types aren’t atomic and why standard
|
|
|
|
|
library types aren’t implemented to use `Arc<T>` by default. The reason is that
|
|
|
|
|
thread safety comes with a performance penalty that you only want to pay when
|
|
|
|
|
you really need to. If you’re just performing operations on values within a
|
|
|
|
|
single thread, your code can run faster if it doesn’t have to enforce the
|
|
|
|
|
guarantees atomics provide.
|
|
|
|
|
|
|
|
|
|
Let’s return to our example: `Arc<T>` and `Rc<T>` have the same API, so we fix
|
|
|
|
|
our program by changing the `use` line, the call to `new`, and the call to
|
2022-08-26 20:13:02 +00:00
|
|
|
|
`clone`. The code in Listing 16-15 will finally compile and run.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
use std::thread;
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let counter = Arc::new(Mutex::new(0));
|
|
|
|
|
let mut handles = vec![];
|
|
|
|
|
|
|
|
|
|
for _ in 0..10 {
|
|
|
|
|
let counter = Arc::clone(&counter);
|
|
|
|
|
let handle = thread::spawn(move || {
|
|
|
|
|
let mut num = counter.lock().unwrap();
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
*num += 1;
|
|
|
|
|
});
|
|
|
|
|
handles.push(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for handle in handles {
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
}
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|
2022-01-27 02:44:28 +00:00
|
|
|
|
println!("Result: {}", *counter.lock().unwrap());
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 16-15: Using an `Arc<T>` to wrap the `Mutex<T>` to be able to share
|
|
|
|
|
ownership across multiple threads
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
This code will print the following:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Result: 10
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We did it! We counted from 0 to 10, which may not seem very impressive, but it
|
|
|
|
|
did teach us a lot about `Mutex<T>` and thread safety. You could also use this
|
|
|
|
|
program’s structure to do more complicated operations than just incrementing a
|
|
|
|
|
counter. Using this strategy, you can divide a calculation into independent
|
|
|
|
|
parts, split those parts across threads, and then use a `Mutex<T>` to have each
|
|
|
|
|
thread update the final result with its part.
|
|
|
|
|
|
2022-06-06 00:29:10 +00:00
|
|
|
|
Note that if you are doing simple numerical operations, there are types simpler
|
|
|
|
|
than `Mutex<T>` types provided by the `std::sync::atomic` module of the
|
|
|
|
|
standard library. These types provide safe, concurrent, atomic access to
|
|
|
|
|
primitive types. We chose to use `Mutex<T>` with a primitive type for this
|
|
|
|
|
example so we could concentrate on how `Mutex<T>` works.
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
### Similarities Between RefCell<T>/Rc<T> and Mutex<T>/Arc<T>
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
You might have noticed that `counter` is immutable but we could get a mutable
|
|
|
|
|
reference to the value inside it; this means `Mutex<T>` provides interior
|
|
|
|
|
mutability, as the `Cell` family does. In the same way we used `RefCell<T>` in
|
|
|
|
|
Chapter 15 to allow us to mutate contents inside an `Rc<T>`, we use `Mutex<T>`
|
|
|
|
|
to mutate contents inside an `Arc<T>`.
|
|
|
|
|
|
|
|
|
|
Another detail to note is that Rust can’t protect you from all kinds of logic
|
|
|
|
|
errors when you use `Mutex<T>`. Recall in Chapter 15 that using `Rc<T>` came
|
|
|
|
|
with the risk of creating reference cycles, where two `Rc<T>` values refer to
|
|
|
|
|
each other, causing memory leaks. Similarly, `Mutex<T>` comes with the risk of
|
|
|
|
|
creating *deadlocks*. These occur when an operation needs to lock two resources
|
|
|
|
|
and two threads have each acquired one of the locks, causing them to wait for
|
|
|
|
|
each other forever. If you’re interested in deadlocks, try creating a Rust
|
|
|
|
|
program that has a deadlock; then research deadlock mitigation strategies for
|
|
|
|
|
mutexes in any language and have a go at implementing them in Rust. The
|
|
|
|
|
standard library API documentation for `Mutex<T>` and `MutexGuard` offers
|
|
|
|
|
useful information.
|
|
|
|
|
|
|
|
|
|
We’ll round out this chapter by talking about the `Send` and `Sync` traits and
|
|
|
|
|
how we can use them with custom types.
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
## Extensible Concurrency with the Send and Sync Traits
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Interestingly, the Rust language has *very* few concurrency features. Almost
|
|
|
|
|
every concurrency feature we’ve talked about so far in this chapter has been
|
|
|
|
|
part of the standard library, not the language. Your options for handling
|
|
|
|
|
concurrency are not limited to the language or the standard library; you can
|
|
|
|
|
write your own concurrency features or use those written by others.
|
|
|
|
|
|
|
|
|
|
However, two concurrency concepts are embedded in the language: the
|
2022-08-26 20:13:02 +00:00
|
|
|
|
`std::marker` traits `Send` and `Sync` .
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
### Allowing Transference of Ownership Between Threads with Send
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-05-02 00:11:23 +00:00
|
|
|
|
The `Send` marker trait indicates that ownership of values of the type
|
|
|
|
|
implementing `Send` can be transferred between threads. Almost every Rust type
|
|
|
|
|
is `Send`, but there are some exceptions, including `Rc<T>`: this cannot be
|
|
|
|
|
`Send` because if you cloned an `Rc<T>` value and tried to transfer ownership
|
|
|
|
|
of the clone to another thread, both threads might update the reference count
|
|
|
|
|
at the same time. For this reason, `Rc<T>` is implemented for use in
|
|
|
|
|
single-threaded situations where you don’t want to pay the thread-safe
|
|
|
|
|
performance penalty.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Therefore, Rust’s type system and trait bounds ensure that you can never
|
|
|
|
|
accidentally send an `Rc<T>` value across threads unsafely. When we tried to do
|
2022-09-13 16:54:09 +00:00
|
|
|
|
this in Listing 16-14, we got the error `the trait `Send` is not implemented
|
|
|
|
|
for `Rc<Mutex<i32>>``. When we switched to `Arc<T>`, which is `Send`, the code
|
|
|
|
|
compiled.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Any type composed entirely of `Send` types is automatically marked as `Send` as
|
|
|
|
|
well. Almost all primitive types are `Send`, aside from raw pointers, which
|
|
|
|
|
we’ll discuss in Chapter 19.
|
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
### Allowing Access from Multiple Threads with Sync
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
The `Sync` marker trait indicates that it is safe for the type implementing
|
|
|
|
|
`Sync` to be referenced from multiple threads. In other words, any type `T` is
|
|
|
|
|
`Sync` if `&T` (an immutable reference to `T`) is `Send`, meaning the reference
|
|
|
|
|
can be sent safely to another thread. Similar to `Send`, primitive types are
|
|
|
|
|
`Sync`, and types composed entirely of types that are `Sync` are also `Sync`.
|
|
|
|
|
|
|
|
|
|
The smart pointer `Rc<T>` is also not `Sync` for the same reasons that it’s not
|
|
|
|
|
`Send`. The `RefCell<T>` type (which we talked about in Chapter 15) and the
|
|
|
|
|
family of related `Cell<T>` types are not `Sync`. The implementation of borrow
|
|
|
|
|
checking that `RefCell<T>` does at runtime is not thread-safe. The smart
|
|
|
|
|
pointer `Mutex<T>` is `Sync` and can be used to share access with multiple
|
2022-08-26 20:13:02 +00:00
|
|
|
|
threads, as you saw in “Sharing a Mutex<T> Between Multiple Threads” on page XX.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
2022-08-26 20:13:02 +00:00
|
|
|
|
### Implementing Send and Sync Manually Is Unsafe
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
Because types that are made up of `Send` and `Sync` traits are automatically
|
|
|
|
|
also `Send` and `Sync`, we don’t have to implement those traits manually. As
|
|
|
|
|
marker traits, they don’t even have any methods to implement. They’re just
|
|
|
|
|
useful for enforcing invariants related to concurrency.
|
|
|
|
|
|
|
|
|
|
Manually implementing these traits involves implementing unsafe Rust code.
|
|
|
|
|
We’ll talk about using unsafe Rust code in Chapter 19; for now, the important
|
|
|
|
|
information is that building new concurrent types not made up of `Send` and
|
|
|
|
|
`Sync` parts requires careful thought to uphold the safety guarantees. “The
|
2022-08-26 20:13:02 +00:00
|
|
|
|
Rustonomicon” at *https://doc.rust-lang.org/stable/nomicon* has more
|
2022-05-02 00:11:23 +00:00
|
|
|
|
information about these guarantees and how to uphold them.
|
2022-01-27 02:44:28 +00:00
|
|
|
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
|
|
|
|
This isn’t the last you’ll see of concurrency in this book: the project in
|
|
|
|
|
Chapter 20 will use the concepts in this chapter in a more realistic situation
|
|
|
|
|
than the smaller examples discussed here.
|
|
|
|
|
|
|
|
|
|
As mentioned earlier, because very little of how Rust handles concurrency is
|
|
|
|
|
part of the language, many concurrency solutions are implemented as crates.
|
|
|
|
|
These evolve more quickly than the standard library, so be sure to search
|
|
|
|
|
online for the current, state-of-the-art crates to use in multithreaded
|
|
|
|
|
situations.
|
|
|
|
|
|
|
|
|
|
The Rust standard library provides channels for message passing and smart
|
|
|
|
|
pointer types, such as `Mutex<T>` and `Arc<T>`, that are safe to use in
|
|
|
|
|
concurrent contexts. The type system and the borrow checker ensure that the
|
|
|
|
|
code using these solutions won’t end up with data races or invalid references.
|
|
|
|
|
Once you get your code to compile, you can rest assured that it will happily
|
|
|
|
|
run on multiple threads without the kinds of hard-to-track-down bugs common in
|
|
|
|
|
other languages. Concurrent programming is no longer a concept to be afraid of:
|
|
|
|
|
go forth and make your programs concurrent, fearlessly!
|
|
|
|
|
|
|
|
|
|
Next, we’ll talk about idiomatic ways to model problems and structure solutions
|
|
|
|
|
as your Rust programs get bigger. In addition, we’ll discuss how Rust’s idioms
|
|
|
|
|
relate to those you might be familiar with from object-oriented programming.
|
2022-08-26 20:13:02 +00:00
|
|
|
|
|