- Rust的闭包也就是类似lambda表达式,大致的格式是
|a, b| {...}
,竖线里面的是参数,花括号里面的是函数逻辑; - 通过
thread::spawn(|| {})
产生的线程,括号内的参数实际上就是一个闭包,因为创建新的线程不需要参数,因此就是两个竖线跟着一个花括号里面要做的事;如果闭包里面要用到外面的变量,就必须在前面加上move
关键字,把外面的变量“捕获”(capture)到闭包内部,为什么要move
到闭包内部,因为闭包的逻辑由新产生的线程来完成,他可能比外面的线程活得久(outlive),而Rust总是要求访问的数据是有效的,因此要把外部的变量的所有权移动(move)到闭包内部,如果不想使用move,可以有两个方案,一个是为需要move的变量实现Copy
特征,另一个是把要移动的变量设置为Static
; - 当spawn了多个线程,并把handle放到一个
Vec<thread::JoinHandle<()>>
中时,如果要join所有的handle,书上不建议用for loop,而是while let Some(handle) = handlers.pop() {}
,因为for循环不允许对遍历的变量进行修改而while可以,(实际上for循环现在也可以修改); - 闭包实际上是一个匿名struct,实现了
std::ops::FnOnce
特征,可能实现了std::ops::Fn
或者std::ops::FnMut
;函数(function)实际上是一个函数指针(function pointer),函数指针指向的是代码(code)而不是数据(data),在这种场景下,代码(code)是被标记为可执行(executable),在一些复杂的情况下,没有捕获参数的闭包也是函数指针; - 多线程访问同一个变量的方法最常用的是用
Arc<Mutex<T>>
来包裹住要访问的数据T
;另一种是采用Channels
来制造一个发送端Sender
和接收端Receiver
; Channel
是单向传输的,如果需要双向传输(duplex),书上的建议是创建两个Channel,每个channel对应一个方向,channel可以用于实现一个任务队列(task queue),详见Page358;- 任务的规模和并发度是有一个取舍的,在10.5节的图中有一个简单的阐述,最左边的Green Thread可以理解为是协程8. 10.5节分别从小到大讲了几个计算机处理任务的单元,从线程、进程、WebAssembly、容器(container)、操作系统,值得注意的是,WASM比进程更大一级,在web这个运行时里,每一个WASM模块里的程序都是隔离的(体现在内存隔离、任务不相干扰),这一点跟进程类似,但WASM又运行在浏览器上,进程是在操作系统上,这一点可以看出WASM处于进程和操作系统之间;容器(Containers)有点类似K8s中的pod概念,进程共享一个文件系统(file system),而Containers自带文件系统;