Skip to content

理解Future

https://stevenbai.top/rust/futures_explained_in_200_lines_of_rust/

线程

现在,实现这一点的一个方法就是让操作系统为我们处理好一切。 我们只需为每个要完成的任务生成一个新的操作系统线程,并像通常那样编写代码。

我们用来处理并发性的运行时就是操作系统本身。

优点:

  • 简单
  • 易用
  • 在任务之间切换相当快
  • 不需要付出即可得到并行支持

缺点:

  • 操作系统级线程的堆栈相当大。 如果同时有许多任务等待(就像在负载很重的 web 服务器中那样) ,那么内存将很快耗尽
  • 这涉及到很多系统调用。当任务数量很高时,这可能会非常昂贵
  • 操作系统有很多事情需要处理。 它可能不会像你希望的那样快速地切换回线程
  • 某些系统可能不支持线程
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
use chrono::prelude::*;
use std::thread;

fn main() {
    println!("begin time = {}", Local::now());
    println!("So we start the program here!");
    let t1 = thread::spawn(move || {
        thread::sleep(std::time::Duration::from_millis(200));
        println!("We create tasks which gets run when they're finished!");
        println!("time1 = {}", Local::now());
    });

    let t2 = thread::spawn(move || {
        thread::sleep(std::time::Duration::from_millis(100));
        println!("We can even chain callbacks...");
        println!("time2 = {}", Local::now());
        let t3 = thread::spawn(move || {
            thread::sleep(std::time::Duration::from_millis(50));
            println!("...like this!");
            println!("time3 = {}", Local::now());
        });
        t3.join().unwrap();
    });
    println!("While our tasks are executing we can do other stuff here.");

    t1.join().unwrap();
    t2.join().unwrap();
    println!("end time = {}", Local::now());
}
// begin time = 2020-04-19 23:32:32.616433 +08:00
// So we start the program here!
// While our tasks are executing we can do other stuff here.
// We can even chain callbacks...
// time2 = 2020-04-19 23:32:32.721725 +08:00
// ...like this!
// time3 = 2020-04-19 23:32:32.777009 +08:00
// We create tasks which gets run when they're finished!
// time1 = 2020-04-19 23:32:32.821411 +08:00
// end time = 2020-04-19 23:32:32.821515 +08:00

操作系统线程肯定有一些相当大的优势。 这也是为什么所有这些讨论“异步”和并发性把线程摆在首位?

首先。 为了使计算机有效率,它们需要多任务处理。 一旦你开始深入研究(比如操作系统是如何工作的) ,你就会发现并发无处不在。 这是我们做任何事情的基础。

其次,我们有网络。

Webservers 是关于I/O和处理小任务(请求)的。
当小任务的数量很大时,由于它们所需的内存和创建新线程所涉及的开销,就不适合今天的操作系统线程。

如果负载是可变的,那么问题就更大了,
这意味着程序在任何时间点的当前任务数量都是不可预测的。
这就是为什么今天你会看到如此多的异步web框架和数据库驱动程序。

然而有大量的问题,标准的线程通常是正确的解决方案。
因此在使用异步库之前,请三思而后行。

现在,让我们来看看多任务处理的其他选项。
它们都有一个共同点,那就是它们实现了一种多任务处理的方式,即拥有一个“用户界面”运行时:

运行时(Runtime)

像 c#,JavaScript,Java,GO 和许多其他语言都有一个处理并发的运行时。
所以如果你来自这些语言中的一种,这对你来说可能会有点奇怪。

Rust 与这些语言的不同之处在于 Rust 没有处理并发性的运行时,因此您需要使用一个为您提供此功能的库。

很多复杂性归因于 Futures 实际上是来源于运行时的复杂性,创建一个有效的运行时是困难的。
学习如何正确使用一个也需要相当多的努力,但是你会看到这些类型的运行时之间有几个相似之处,所以学习一个可以使学习下一个更容易。

Rust 和其他语言的区别在于,在选择运行时时,您必须进行主动选择。大多数情况下,在其他语言中,你只会使用提供给你的那一种。