1
- % The Rust Tasks and Communication Guide
1
+ % The Rust Threads and Communication Guide
2
2
3
- ** NOTE** This guide is badly out of date an needs to be rewritten.
3
+ ** NOTE** This guide is badly out of date and needs to be rewritten.
4
4
5
5
# Introduction
6
6
@@ -9,36 +9,36 @@ primitives. This guide will describe the concurrency model in Rust, how it
9
9
relates to the Rust type system, and introduce the fundamental library
10
10
abstractions for constructing concurrent programs.
11
11
12
- Tasks provide failure isolation and recovery. When a fatal error occurs in Rust
12
+ Threads provide failure isolation and recovery. When a fatal error occurs in Rust
13
13
code as a result of an explicit call to ` panic!() ` , an assertion failure, or
14
- another invalid operation, the runtime system destroys the entire task . Unlike
14
+ another invalid operation, the runtime system destroys the entire thread . Unlike
15
15
in languages such as Java and C++, there is no way to ` catch ` an exception.
16
- Instead, tasks may monitor each other to see if they panic.
16
+ Instead, threads may monitor each other to see if they panic.
17
17
18
- Tasks use Rust's type system to provide strong memory safety guarantees. In
19
- particular, the type system guarantees that tasks cannot induce a data race
18
+ Threads use Rust's type system to provide strong memory safety guarantees. In
19
+ particular, the type system guarantees that threads cannot induce a data race
20
20
from shared mutable state.
21
21
22
22
# Basics
23
23
24
- At its simplest, creating a task is a matter of calling the ` spawn ` function
25
- with a closure argument. ` spawn ` executes the closure in the new task .
24
+ At its simplest, creating a thread is a matter of calling the ` spawn ` function
25
+ with a closure argument. ` spawn ` executes the closure in the new thread .
26
26
27
27
``` {rust,ignore}
28
- # use std::task ::spawn;
28
+ # use std::thread ::spawn;
29
29
30
- // Print something profound in a different task using a named function
31
- fn print_message() { println!("I am running in a different task !"); }
30
+ // Print something profound in a different thread using a named function
31
+ fn print_message() { println!("I am running in a different thread !"); }
32
32
spawn(print_message);
33
33
34
34
// Alternatively, use a `move ||` expression instead of a named function.
35
35
// `||` expressions evaluate to an unnamed closure. The `move` keyword
36
36
// indicates that the closure should take ownership of any variables it
37
37
// touches.
38
- spawn(move || println!("I am also running in a different task !"));
38
+ spawn(move || println!("I am also running in a different thread !"));
39
39
```
40
40
41
- In Rust, a task is not a concept that appears in the language semantics.
41
+ In Rust, a thread is not a concept that appears in the language semantics.
42
42
Instead, Rust's type system provides all the tools necessary to implement safe
43
43
concurrency: particularly, ownership. The language leaves the implementation
44
44
details to the standard library.
@@ -49,26 +49,26 @@ argument a closure (of type `F`) that it will run exactly once. This
49
49
closure is limited to capturing ` Send ` -able data from its environment
50
50
(that is, data which is deeply owned). Limiting the closure to ` Send `
51
51
ensures that ` spawn ` can safely move the entire closure and all its
52
- associated state into an entirely different task for execution.
52
+ associated state into an entirely different thread for execution.
53
53
54
54
``` {rust,ignore}
55
- # use std::task ::spawn;
56
- # fn generate_task_number () -> int { 0 }
55
+ # use std::thread ::spawn;
56
+ # fn generate_thread_number () -> int { 0 }
57
57
// Generate some state locally
58
- let child_task_number = generate_task_number ();
58
+ let child_thread_number = generate_thread_number ();
59
59
60
60
spawn(move || {
61
- // Capture it in the remote task . The `move` keyword indicates
62
- // that this closure should move `child_task_number ` into its
61
+ // Capture it in the remote thread . The `move` keyword indicates
62
+ // that this closure should move `child_thread_number ` into its
63
63
// environment, rather than capturing a reference into the
64
64
// enclosing stack frame.
65
- println!("I am child number {}", child_task_number );
65
+ println!("I am child number {}", child_thread_number );
66
66
});
67
67
```
68
68
69
69
## Communication
70
70
71
- Now that we have spawned a new task , it would be nice if we could communicate
71
+ Now that we have spawned a new thread , it would be nice if we could communicate
72
72
with it. For this, we use * channels* . A channel is simply a pair of endpoints:
73
73
one for sending messages and another for receiving messages.
74
74
@@ -78,7 +78,7 @@ of a channel, and a **receiver** is the receiving endpoint. Consider the followi
78
78
example of calculating two results concurrently:
79
79
80
80
``` {rust,ignore}
81
- # use std::task ::spawn;
81
+ # use std::thread ::spawn;
82
82
83
83
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
84
84
@@ -102,12 +102,12 @@ into its component parts).
102
102
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
103
103
```
104
104
105
- The child task will use the sender to send data to the parent task , which will
105
+ The child thread will use the sender to send data to the parent thread , which will
106
106
wait to receive the data on the receiver. The next statement spawns the child
107
- task .
107
+ thread .
108
108
109
109
``` {rust,ignore}
110
- # use std::task ::spawn;
110
+ # use std::thread ::spawn;
111
111
# fn some_expensive_computation() -> int { 42 }
112
112
# let (tx, rx) = channel();
113
113
spawn(move || {
@@ -116,10 +116,10 @@ spawn(move || {
116
116
});
117
117
```
118
118
119
- Notice that the creation of the task closure transfers ` tx ` to the child task
119
+ Notice that the creation of the thread closure transfers ` tx ` to the child thread
120
120
implicitly: the closure captures ` tx ` in its environment. Both ` Sender ` and
121
- ` Receiver ` are sendable types and may be captured into tasks or otherwise
122
- transferred between them. In the example, the child task runs an expensive
121
+ ` Receiver ` are sendable types and may be captured into threads or otherwise
122
+ transferred between them. In the example, the child thread runs an expensive
123
123
computation, then sends the result over the captured channel.
124
124
125
125
Finally, the parent continues with some other expensive computation, then waits
@@ -137,7 +137,7 @@ The `Sender` and `Receiver` pair created by `channel` enables efficient
137
137
communication between a single sender and a single receiver, but multiple
138
138
senders cannot use a single ` Sender ` value, and multiple receivers cannot use a
139
139
single ` Receiver ` value. What if our example needed to compute multiple
140
- results across a number of tasks ? The following program is ill-typed:
140
+ results across a number of threads ? The following program is ill-typed:
141
141
142
142
``` {rust,ignore}
143
143
# fn some_expensive_computation() -> int { 42 }
@@ -160,7 +160,7 @@ Instead we can clone the `tx`, which allows for multiple senders.
160
160
let (tx, rx) = channel();
161
161
162
162
for init_val in range(0u, 3) {
163
- // Create a new channel handle to distribute to the child task
163
+ // Create a new channel handle to distribute to the child thread
164
164
let child_tx = tx.clone();
165
165
spawn(move || {
166
166
child_tx.send(some_expensive_computation(init_val));
@@ -172,7 +172,7 @@ let result = rx.recv() + rx.recv() + rx.recv();
172
172
```
173
173
174
174
Cloning a ` Sender ` produces a new handle to the same channel, allowing multiple
175
- tasks to send data to a single receiver. It upgrades the channel internally in
175
+ threads to send data to a single receiver. It upgrades the channel internally in
176
176
order to allow this functionality, which means that channels that are not
177
177
cloned can avoid the overhead required to handle multiple senders. But this
178
178
fact has no bearing on the channel's usage: the upgrade is transparent.
@@ -182,9 +182,9 @@ simply use three `Sender` pairs, but it serves to illustrate the point. For
182
182
reference, written with multiple streams, it might look like the example below.
183
183
184
184
``` {rust,ignore}
185
- # use std::task ::spawn;
185
+ # use std::thread ::spawn;
186
186
187
- // Create a vector of ports, one for each child task
187
+ // Create a vector of ports, one for each child thread
188
188
let rxs = Vec::from_fn(3, |init_val| {
189
189
let (tx, rx) = channel();
190
190
spawn(move || {
@@ -256,18 +256,18 @@ fn main() {
256
256
257
257
## Sharing without copying: Arc
258
258
259
- To share data between tasks , a first approach would be to only use channel as
259
+ To share data between threads , a first approach would be to only use channel as
260
260
we have seen previously. A copy of the data to share would then be made for
261
- each task . In some cases, this would add up to a significant amount of wasted
261
+ each thread . In some cases, this would add up to a significant amount of wasted
262
262
memory and would require copying the same data more than necessary.
263
263
264
264
To tackle this issue, one can use an Atomically Reference Counted wrapper
265
265
(` Arc ` ) as implemented in the ` sync ` library of Rust. With an Arc, the data
266
- will no longer be copied for each task . The Arc acts as a reference to the
266
+ will no longer be copied for each thread . The Arc acts as a reference to the
267
267
shared data and only this reference is shared and cloned.
268
268
269
269
Here is a small example showing how to use Arcs. We wish to run concurrently
270
- several computations on a single large vector of floats. Each task needs the
270
+ several computations on a single large vector of floats. Each thread needs the
271
271
full vector to perform its duty.
272
272
273
273
``` {rust,ignore}
@@ -284,10 +284,10 @@ fn main() {
284
284
let numbers_arc = Arc::new(numbers);
285
285
286
286
for num in range(1u, 10) {
287
- let task_numbers = numbers_arc.clone();
287
+ let thread_numbers = numbers_arc.clone();
288
288
289
289
spawn(move || {
290
- println!("{}-norm = {}", num, pnorm(task_numbers .as_slice(), num));
290
+ println!("{}-norm = {}", num, pnorm(thread_numbers .as_slice(), num));
291
291
});
292
292
}
293
293
}
@@ -306,8 +306,8 @@ let numbers_arc = Arc::new(numbers);
306
306
# }
307
307
```
308
308
309
- and a clone is captured for each task via a procedure. This only copies
310
- the wrapper and not its contents. Within the task 's procedure, the captured
309
+ and a clone is captured for each thread via a procedure. This only copies
310
+ the wrapper and not its contents. Within the thread 's procedure, the captured
311
311
Arc reference can be used as a shared reference to the underlying vector as
312
312
if it were local.
313
313
@@ -319,29 +319,29 @@ if it were local.
319
319
# let numbers=Vec::from_fn(1000000, |_| rand::random::<f64>());
320
320
# let numbers_arc = Arc::new(numbers);
321
321
# let num = 4;
322
- let task_numbers = numbers_arc.clone();
322
+ let thread_numbers = numbers_arc.clone();
323
323
spawn(move || {
324
- // Capture task_numbers and use it as if it was the underlying vector
325
- println!("{}-norm = {}", num, pnorm(task_numbers .as_slice(), num));
324
+ // Capture thread_numbers and use it as if it was the underlying vector
325
+ println!("{}-norm = {}", num, pnorm(thread_numbers .as_slice(), num));
326
326
});
327
327
# }
328
328
```
329
329
330
- # Handling task panics
330
+ # Handling thread panics
331
331
332
332
Rust has a built-in mechanism for raising exceptions. The ` panic!() ` macro
333
333
(which can also be written with an error string as an argument: `panic!(
334
334
~ reason)` ) and the ` assert!` construct (which effectively calls ` panic!()` if a
335
- boolean expression is false) are both ways to raise exceptions. When a task
336
- raises an exception, the task unwinds its stack—running destructors and
335
+ boolean expression is false) are both ways to raise exceptions. When a thread
336
+ raises an exception, the thread unwinds its stack—running destructors and
337
337
freeing memory along the way—and then exits. Unlike exceptions in C++,
338
- exceptions in Rust are unrecoverable within a single task : once a task panics,
338
+ exceptions in Rust are unrecoverable within a single thread : once a thread panics,
339
339
there is no way to "catch" the exception.
340
340
341
- While it isn't possible for a task to recover from panicking, tasks may notify
341
+ While it isn't possible for a thread to recover from panicking, threads may notify
342
342
each other if they panic. The simplest way of handling a panic is with the
343
343
` try ` function, which is similar to ` spawn ` , but immediately blocks and waits
344
- for the child task to finish. ` try ` returns a value of type
344
+ for the child thread to finish. ` try ` returns a value of type
345
345
` Result<T, Box<Any + Send>> ` . ` Result ` is an ` enum ` type with two variants:
346
346
` Ok ` and ` Err ` . In this case, because the type arguments to ` Result ` are ` int `
347
347
and ` () ` , callers can pattern-match on a result to check whether it's an ` Ok `
@@ -364,19 +364,19 @@ assert!(result.is_err());
364
364
365
365
Unlike ` spawn ` , the function spawned using ` try ` may return a value, which
366
366
` try ` will dutifully propagate back to the caller in a [ ` Result ` ] enum. If the
367
- child task terminates successfully, ` try ` will return an ` Ok ` result; if the
368
- child task panics, ` try ` will return an ` Error ` result.
367
+ child thread terminates successfully, ` try ` will return an ` Ok ` result; if the
368
+ child thread panics, ` try ` will return an ` Error ` result.
369
369
370
370
[ `Result` ] : std/result/index.html
371
371
372
- > * Note:* A panicked task does not currently produce a useful error
372
+ > * Note:* A panicked thread does not currently produce a useful error
373
373
> value (` try ` always returns ` Err(()) ` ). In the
374
- > future, it may be possible for tasks to intercept the value passed to
374
+ > future, it may be possible for threads to intercept the value passed to
375
375
> ` panic!() ` .
376
376
377
377
But not all panics are created equal. In some cases you might need to abort
378
378
the entire program (perhaps you're writing an assert which, if it trips,
379
379
indicates an unrecoverable logic error); in other cases you might want to
380
380
contain the panic at a certain boundary (perhaps a small piece of input from
381
381
the outside world, which you happen to be processing in parallel, is malformed
382
- such that the processing task cannot proceed).
382
+ such that the processing thread cannot proceed).
0 commit comments