2024-05-06 20:40:05 +00:00
|
|
|
---
|
2024-10-30 17:34:11 +00:00
|
|
|
date: 2020-09-30
|
2024-05-06 20:40:05 +00:00
|
|
|
id: 90fe2657-017e-41f6-9ac3-e4cb4590dc44
|
|
|
|
title: Rust Threads
|
|
|
|
---
|
|
|
|
|
|
|
|
# Introduction
|
|
|
|
|
|
|
|
Programming languages implement threads in a few different ways. Many
|
|
|
|
operating systems provide an API for creating new threads. This model
|
|
|
|
where a language calls the operating system APIs to create threads is
|
|
|
|
sometimes called *1:1*, meaning one operating system thread per one
|
|
|
|
language thread.
|
|
|
|
|
|
|
|
Many programming languages provide their own special implementation of
|
|
|
|
threads. Programming language-provided threads are known as green
|
|
|
|
threads, and languages that use these green threads will execute them in
|
|
|
|
the context of a different number of operating system threads. For this
|
|
|
|
reason, the green-threaded model is called the *M:N* model: there are M
|
|
|
|
green threads per N operating system threads, where M and N are not
|
|
|
|
necessarily the same number.
|
|
|
|
|
|
|
|
Each model has its own advantages and trade-offs, and the trade-off most
|
|
|
|
important to Rust is runtime support. Runtime is a confusing term and
|
|
|
|
can have different meanings in different contexts.
|
|
|
|
|
|
|
|
In this context, by *runtime* we mean code that is included by the
|
|
|
|
language in every binary. This code can be large or small depending on
|
|
|
|
the language, but every non-assembly language will have some amount of
|
|
|
|
runtime code. For that reason, colloquially when people say a language
|
|
|
|
has “no runtime,” they often mean “small runtime.” Smaller runtimes have
|
|
|
|
fewer features but have the advantage of resulting in smaller binaries,
|
|
|
|
which make it easier to combine the language with other languages in
|
|
|
|
more contexts. Although many languages are okay with increasing the
|
|
|
|
runtime size in exchange for more features, Rust needs to have nearly no
|
|
|
|
runtime and cannot compromise on being able to call into C to maintain
|
|
|
|
performance.
|
|
|
|
|
|
|
|
The green-threading M:N model requires a larger language runtime to
|
|
|
|
manage threads. As such, the Rust standard library only provides an
|
|
|
|
implementation of 1:1 threading. Because Rust is such a low-level
|
|
|
|
language, there are crates that implement M:N threading if you would
|
|
|
|
rather trade overhead for aspects such as more control over which
|
|
|
|
threads run when and lower costs of context switching, for example.
|
|
|
|
|
|
|
|
# Examples
|
|
|
|
|
|
|
|
## Creating a New Thread
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
thread::spawn(|| {
|
|
|
|
for i in 1..10 {
|
|
|
|
println!("hi number {} from the spawned thread!", i);
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for i in 1..5 {
|
|
|
|
println!("hi number {} from the main thread!", i);
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## Waiting for All Threads To Finish
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let handle = thread::spawn(|| {
|
|
|
|
for i in 1..10 {
|
|
|
|
println!("hi number {} from the spawned thread!", i);
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for i in 1..5 {
|
|
|
|
println!("hi number {} from the main thread!", i);
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let handle = thread::spawn(|| {
|
|
|
|
for i in 1..10 {
|
|
|
|
println!("hi number {} from the spawned thread!", i);
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
|
|
|
for i in 1..5 {
|
|
|
|
println!("hi number {} from the main thread!", i);
|
|
|
|
thread::sleep(Duration::from_millis(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## using move Closures
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
use std::thread;
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let v = vec![1, 2, 3];
|
|
|
|
|
|
|
|
let handle = thread::spawn(move || {
|
|
|
|
println!("Here's a vector: {:?}", v);
|
|
|
|
});
|
|
|
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
}
|
|
|
|
```
|