Читаем Rust by Example полностью

The aliasing rules (one writable reference XOR many readable references) automatically prevent you from manipulating state that is visible to other threads. (Where synchronisation is needed, there are synchronisation primitives like Mutexes or Channels.)

In this example, we will calculate the sum of all digits in a block of numbers. We will do this by parcelling out chunks of the block into different threads. Each thread will sum its tiny block of digits, and subsequently we will sum the intermediate sums produced by each thread.

Note that, although we're passing references across thread boundaries, Rust understands that we're only passing read-only references, and that thus no unsafety or data races can occur. Because we're move-ing the data segments into the thread, Rust will also ensure the data is kept alive until the threads exit, so no dangling pointers occur.

use std::thread;

// This is the `main` thread

fn main() {

// This is our data to process.

// We will calculate the sum of all digits via a threaded map-reduce algorithm.

// Each whitespace separated chunk will be handled in a different thread.

//

// TODO: see what happens to the output if you insert spaces!

let data = "86967897737416471853297327050364959

11861322575564723963297542624962850

70856234701860851907960690014725639

38397966707106094172783238747669219

52380795257888236525459303330302837

58495327135744041048897885734297812

69920216438980873548808413720956532

16278424637452589860345374828574668";

// Make a vector to hold the child-threads which we will spawn.

let mut children = vec![];

/*************************************************************************

* "Map" phase

*

* Divide our data into segments, and apply initial processing

************************************************************************/

// split our data into segments for individual calculation

// each chunk will be a reference (&str) into the actual data

let chunked_data = data.split_whitespace();

// Iterate over the data segments.

// .enumerate() adds the current loop index to whatever is iterated

// the resulting tuple "(index, element)" is then immediately

// "destructured" into two variables, "i" and "data_segment" with a

// "destructuring assignment"

for (i, data_segment) in chunked_data.enumerate() {

println!("data segment {} is \"{}\"", i, data_segment);

// Process each data segment in a separate thread

//

// spawn() returns a handle to the new thread,

// which we MUST keep to access the returned value

//

// 'move || -> u32' is syntax for a closure that:

// * takes no arguments ('||')

// * takes ownership of its captured variables ('move') and

// * returns an unsigned 32-bit integer ('-> u32')

//

// Rust is smart enough to infer the '-> u32' from

// the closure itself so we could have left that out.

//

// TODO: try removing the 'move' and see what happens

children.push(thread::spawn(move || -> u32 {

// Calculate the intermediate sum of this segment:

let result = data_segment

Перейти на страницу:

Похожие книги