「Rust中的多线程」1

464 阅读3分钟

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


多线程的总体思路是在较短的时间内完成更多的工作。主要是通过将代码分割成多个部分来实现的,这些部分被称为:线程

然后,这些线程被并发执行(甚至可能是并行的)。

这些线程必须(基本上)是相互独立的。但是对于依赖不同线程的部分代码,需要采取预防措施。这就是编写多线程代码经常被认为是"非常困难"的原因。很多多线程bug是非常微妙的,而且很难追到。

对于Rust的程序员来说,幸运的是,该语言的主要目标之一是使并发编程安全和高效。Rust语言使许多这样的bug无法编写。不正确的代码会拒绝编译,并呈现一个解释问题的错误。

这篇文章通过解决一个exercism编程练习,介绍了一些编写多线程代码的方法。

创建线程

一个线程是通过调用 std::thread::spawn 创建的。它是一个接收闭包的函数。

该闭包包含将在线程中运行的代码。

当线程被创建时,它就与创建它的线程"分离"了。这意味着它是完全独立的,并且可以继续存在它的父线程死亡之后(除非创建者是主线程,如果主线程停止,一切都会停止)。

这种"可能超越父线程"意味着传递给闭包的所有东西必须在整个程序中保持有效(这意味着:它有一个"静态寿命")。这确保了线程中的一切都保持有效,即使父线程已经不存在了。

在实践中,这意味着你希望闭包对它所使用的每个变量拥有所有权。这可以通过在闭包的参数列表前使用 move 关键字来实现。

让一个父线程等待它所产生的线程的完成是可能的。

std::thread::spawn 的调用会返回一个 JoinHandle。它有一个 join() 方法,可以阻断当前线程,等待产生的线程执行完毕后再继续父线程执行代码。

use std::thread;

fn main() {
    let handle = thread::spawn(move || {
        // some work here
    });
    // some work here
    handle.join();
}

题目

问题描述:
使用并行的方式去计算文本中字母的频率。 并行性是指以并行方式做一些也可以按顺序做的事情。一个常见的例子是计算字母的频率:创建一个函数,返回文本列表中每个字母的总频率,该函数采用了并行计算。

我们需要编写一个名为 frequency()。它接收一个 string slice 和一个 count worker 作为参数。

返回值是一个 HashMap :

  • key: 这些字符串包含的所有字母
  • value: 是该字母出现的次数。

这需要在 worker_count 数量的线程中完成。函数签名如下:

fn frequency(input: &[&str], worker_count: usize) -> HashMap<char, usize>