理解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 和其他语言的区别在于,在选择运行时时,您必须进行主动选择。大多数情况下,在其他语言中,你只会使用提供给你的那一种。