「Rust中的多线程」2

970 阅读2分钟

「这是我参与11月更文挑战的第 2 天,活动详情查看:2021最后一次更文挑战


单线程

下面显示了一个在单线程上完成所有工作的问题的解决方案:

fn frequency(input: &[&str]) -> HashMap<char, usize> {
    let mut map = HashMap::new();

    for line in input {
        for c in line.chars().filter(|c| c.is_alphabetic()) {
            *map.entry(c.to_ascii_lowercase()).or_default() += 1;
        }
    }

    map
}

实现策略

我们将把这个大问题分成几个小问题。

每个问题将在一个线程中解决,其代码与单线程例子中的代码相似。这些小问题的结果必须合并成一个大的结果,这将是频率函数的返回值。对大问题的分解可以通过调用输入参数的 chunks 方法来完成。

input.chunks((input.len() / worker_count).max(1));

其结果是一个长度为 worker_countitertor。每个线程将解决单个块的问题。

我们确保在将数据送入线程之前拥有一个分块中的数据对应的所有权。

chunk.join("")

然后,我们准备创建一个线程,为每个块的对应的问题:

pub fn frequency(input: &[&str], worker_count: usize) -> HashMap<char, usize> {
  // divide the large problem into smaller problems
  let chunks = input.chunks((input.len() / worker_count).max(1));

  for chunk in chunks {
    // collect the data for the current chunk into an owned variable before sending it to the thread.
    let string = chunk.join("");
    thread::spawn(move || {
      // solve the problem for the current chunk
    });
  }

  // combine the solutions
}

Joinhandle

JoinHandle有个内部类型。这意味着我们可以从子线程返回一些东西,然后当它调用 join() 时,父线程可以访问到返回的数据。

use std::collections::HashMap;
use std::thread;

pub fn frequency(input: &[&str], worker_count: usize) -> HashMap<char, usize> {
    let mut result: HashMap<char, usize> = HashMap::new();
    let chunks = input.chunks((input.len() / worker_count).max(1));
    let mut handles = Vec::new();

    for chunk in chunks {
        let string = chunk.join("");
        // return a HashMap from each thread, the JoinHandle wraps this hashmap
        let handle = thread::spawn(move || {
            let mut map: HashMap<char, usize> = HashMap::new();
            for c in string.chars().filter(|c| c.is_alphabetic()) {
                *map.entry(c.to_ascii_lowercase()).or_default() += 1;
            }
            map
        });
        handles.push(handle);
    }

    // wait for each thread to finish and combine every HashMap into the final result
    for handle in handles {
        let map = handle.join().unwrap();
        for (key, value) in map {
            *result.entry(key).or_default() += value;
        }
    }
    result
}