Skip to content

Commit b1435ed

Browse files
committed
Guide: tasks
1 parent e5df5f5 commit b1435ed

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

src/doc/guide.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3626,6 +3626,152 @@ guide](http://doc.rust-lang.org/guide-pointers.html#rc-and-arc).
36263626

36273627
# Tasks
36283628

3629+
Concurrency and parallelism are topics that are of increasing interest to a
3630+
broad subsection of software developers. Modern computers are often multi-core,
3631+
to the point that even embedded devices like cell phones have more than one
3632+
processor. Rust's semantics lend themselves very nicely to solving a number of
3633+
issues that programmers have with concurrency. Many concurrency errors that are
3634+
runtime errors in other languages are compile-time errors in Rust.
3635+
3636+
Rust's concurrency primitive is called a **task**. Tasks are lightweight, and
3637+
do not share memory in an unsafe manner, preferring message passing to
3638+
communicate. It's worth noting that tasks are implemented as a library, and
3639+
not part of the language. This means that in the future, other concurrency
3640+
libraries can be written for Rust to help in specific scenarios. Here's an
3641+
example of creating a task:
3642+
3643+
```{rust}
3644+
spawn(proc() {
3645+
println!("Hello from a task!");
3646+
});
3647+
```
3648+
3649+
The `spawn` function takes a proc as an argument, and runs that proc in a new
3650+
task. A proc takes ownership of its entire environment, and so any variables
3651+
that you use inside the proc will not be usable afterward:
3652+
3653+
```{rust,ignore}
3654+
let mut x = vec![1i, 2i, 3i];
3655+
3656+
spawn(proc() {
3657+
println!("The value of x[0] is: {}", x[0]);
3658+
});
3659+
3660+
println!("The value of x[0] is: {}", x[0]); // error: use of moved value: `x`
3661+
```
3662+
3663+
`x` is now owned by the proc, and so we can't use it anymore. Many other
3664+
languages would let us do this, but it's not safe to do so. Rust's type system
3665+
catches the error.
3666+
3667+
If tasks were only able to capture these values, they wouldn't be very useful.
3668+
Luckily, tasks can communicate with each other through **channel**s. Channels
3669+
work like this:
3670+
3671+
```{rust}
3672+
let (tx, rx) = channel();
3673+
3674+
spawn(proc() {
3675+
tx.send("Hello from a task!".to_string());
3676+
});
3677+
3678+
let message = rx.recv();
3679+
println!("{}", message);
3680+
```
3681+
3682+
The `channel()` function returns two endpoints: a `Receiver<T>` and a
3683+
`Sender<T>`. You can use the `.send()` method on the `Sender<T>` end, and
3684+
receive the message on the `Receiver<T>` side with the `recv()` method. This
3685+
method blocks until it gets a message. There's a similar method, `.try_recv()`,
3686+
which returns an `Option<T>` and does not block.
3687+
3688+
If you want to send messages to the task as well, create two channels!
3689+
3690+
```{rust}
3691+
let (tx1, rx1) = channel();
3692+
let (tx2, rx2) = channel();
3693+
3694+
spawn(proc() {
3695+
tx1.send("Hello from a task!".to_string());
3696+
let message = rx2.recv();
3697+
println!("{}", message);
3698+
});
3699+
3700+
let message = rx1.recv();
3701+
println!("{}", message);
3702+
3703+
tx2.send("Goodbye from main!".to_string());
3704+
```
3705+
3706+
The proc has one sending end and one receiving end, and the main task has one
3707+
of each as well. Now they can talk back and forth in whatever way they wish.
3708+
3709+
Notice as well that because `Sender` and `Receiver` are generic, while you can
3710+
pass any kind of information through the channel, the ends are strongly typed.
3711+
If you try to pass a string, and then an integer, Rust will complain.
3712+
3713+
## Futures
3714+
3715+
With these basic primitives, many different concurrency patterns can be
3716+
developed. Rust includes some of these types in its standard library. For
3717+
example, if you wish to compute some value in the background, `Future` is
3718+
a useful thing to use:
3719+
3720+
```{rust}
3721+
use std::sync::Future;
3722+
3723+
let mut delayed_value = Future::spawn(proc() {
3724+
// just return anything for examples' sake
3725+
3726+
12345i
3727+
});
3728+
println!("value = {}", delayed_value.get());
3729+
```
3730+
3731+
Calling `Future::spawn` works just like `spawn()`: it takes a proc. In this
3732+
case, though, you don't need to mess with the channel: just have the proc
3733+
return the value.
3734+
3735+
`Future::spawn` will return a value which we can bind with `let`. It needs
3736+
to be mutable, because once the value is computed, it saves a copy of the
3737+
value, and if it were immutable, it couldn't update itself.
3738+
3739+
The proc will go on processing in the background, and when we need the final
3740+
value, we can call `get()` on it. This will block until the result is done,
3741+
but if it's finished computing in the background, we'll just get the value
3742+
immediately.
3743+
3744+
## Success and failure
3745+
3746+
Tasks don't always succeed, they can also fail. A task that wishes to fail
3747+
can call the `fail!` macro, passing a message:
3748+
3749+
```{rust}
3750+
spawn(proc() {
3751+
fail!("Nope.");
3752+
});
3753+
```
3754+
3755+
If a task fails, it is not possible for it to recover. However, it can
3756+
notify other tasks that it has failed. We can do this with `task::try`:
3757+
3758+
```{rust}
3759+
use std::task;
3760+
use std::rand;
3761+
3762+
let result = task::try(proc() {
3763+
if rand::random() {
3764+
println!("OK");
3765+
} else {
3766+
fail!("oops!");
3767+
}
3768+
});
3769+
```
3770+
3771+
This task will randomly fail or succeed. `task::try` returns a `Result`
3772+
type, so we can handle the response like any other computation that may
3773+
fail.
3774+
36293775
# Macros
36303776

36313777
# Unsafe

0 commit comments

Comments
 (0)