参考章节《Rust语言圣经(Rust Course)》第4.1章 Rust 异步编程
什么是异步编程?
异步编程允许我们同时并发运行大量的任务,却仅仅需要
几个
甚至一个
OS 线程或 CPU 核心
线程和异步编程的区别?
OS 线程非常适合少量任务并发,因为线程的创建和上下文切换是非常昂贵的,甚至于空闲的线程都会消耗系统资源。
虽然
async
和多线程都可以实现并发编程,后者甚至还能通过线程池来增强并发能力,但是这两个方式并不互通
当从一个方式切换成另一个需要大量的代码重构工作(代价比较大)
,因此提前为自己的项目选择适合的并发模型就变得至关重要(提前选择好并发模型至关重要)
。
Rust 选择了 async/await
, 该模型性能高,还能支持底层编程,同时又像线程和协程那样无需过多的改变编程模型,
但有得必有失,async 模型的问题就是内部实现机制过于复杂,对于用户来说,理解和使用起来也没有线程和协程简单,
好在前者的复杂性开发者们已经帮我们封装好
事实上
async
底层也是基于线程实现,但是它基于线程封装了一个运行时
可以将多个任务映射到少量线程上,然后将线程切换变成了任务切换
,后者仅仅是内存中的访问,因此要高效的多。
该使用哪种并发模型?
- 对于长时间运行的
CPU 密集型
任务,例如并行计算,使用线程将更有优势。 - 有部分 IO 任务需要并发运行时,选多线程,如果想要降低线程创建和销毁的开销,可以使用线程池
- 有大量 IO 任务需要并发运行时
(IO密集型)
,选async
模型
总结一下,只有当有大量IO密集型
任务时才选async
,否则统一选多线程
事实上,
async
和多线程并不是二选一,在同一应用中,可以根据情况两者一起使用
async/.await 简单入门
在开始之前,需要先引入 futures
包。编辑 Cargo.toml
文件并添加以下内容:
|
|
定义异步函数使用 async fn
关键字
|
|
需要注意,异步函数的返回值是一个 Future
,若直接调用该函数,不会输出任何结果,因为 Future
还未被执行:
|
|
我们可以使用一个 block_on
执行器来执行 Future
|
|
使用.await
如果你需要在一个异步函数中调用另一个异步函数就需要用.await
|
|
与 block_on
不同,.await
并不会阻塞当前的线程,而是异步的等待Future
的完成,在等待的过程中,该线程还可以继续执行其它的Future
,最终实现了并发处理的效果。
|
|
一个简单的例子
我们有如下三个异步函数
|
|
我们可以这么调用
|
|
以上代码虽然是正确的,但我们仔细观察,发现跳舞和唱歌并不冲突,我们完全可以边跳舞边唱歌啊,因此我们可以利用 .await
来实现这个效果
|
|
总结一下
async
适用于大量IO密集型
任务,其它情况统一选择OS多线程
模型async
模型并不一定优于OS多线程
模型,各自有各自的适用场景async
和OS多线程
并不是二选一,在同一应用中,可以根据情况两者一起使用block_on
是一个执行器,它会阻塞当前线程
直到指定的Future
执行完成.await
并不会阻塞当前的线程,而是异步的等待Future
的完成,在等待的过程中,该线程还可以继续执行其它的Future
futures::join!
可以并发的处理和等待多个Future