Web框架Thruster发布0.5
Niko办公时间:循环服务
讨论了一个经典的问题:Rust如何处理循环数据? 答案是,不需要循环。
当使用带有GC的语言,比如js,可以这么写:
function setup() {
var directory = {};
var service1 = new Service1(directory);
var service2 = new Service2(directory);
return directory;
}
function Service1(directory) {
self.directory = directory;
directory.service1 = self;
...
}
function Service2(directory) {
self.directory = directory;
directory.service2 = self;
...
}
希望有多个服务,并且可以彼此通信,通过创建directory来存储这些服务对象。directory里存储的对象都可以互相访问。
但是将此代码修改为Rust时,就会陷入困惑:
struct Directory {
service1: Option<Service1>,
service2: Option<Service2>,
}
创建Directory时,Service还不存在,所以使用Option。但是初始化directory之后,Service就永远不可能为None。最大的问题是,该如何管理内存?因为Service1有可能会引用到Service2。
使用Arc或原子类型和锁,可以改善,但还不是最好的办法。
#[derive(Clone)]
struct Directory {
service1: Sender<Message1>,
service2: Sender<Message2>,
}
/// Whatever kind of message service1 expects.
struct Message1 { .. }
/// Whatever kind of message service2 expects.
struct Message2 { .. }
可以使用Sender<Message>
类型来解耦,不要通过共享内存来通信,要通过通信来共享内存。
Sender是指Channel中的Sender,利用Channel来实现一个Actor模型,每个service就是一个Actor,Actor之间通过发送消息来相互调用。
Niko使用std::sync::channel来演示这种思想,然后使用Arc、Mutex和ConVar实现了一个简单的自定义Channel。
ABA问题和并发回收
编写无锁数据结构就会出现ABA问题。比如在编写无锁栈的时候:
- 栈里包含A和B
- 线程M开始pop操作
- 线程M 加载 A = top.load()
- 线程M 检查 A ==Null
- 线程M 测试为false,解引用 B = (* A).next
- 线程M被暂停
- 线程M弹出堆栈
- 线程M释放A
- 线程M分配指针C
- 线程N push C
- 线程N分配指针,并且分配器重用A指针
- 线程N push A
- 堆栈现在是A/ C/ B
- 线程N被挂起并且线程M恢复
- 使top.compare_and_swap(A, B)成功
- 现在堆栈B和C被泄漏
前 1 - 13步,实际上在栈顶的A值已经发生过了变化。但是到第15步进行CAS操作的时候,将A值换为B,CAS操作没有分辨出A值实际上已经不是之前的A。所以就发生了ABA问题。
ABA问题的本质是内存回收。对于上面的问题,A被弹出以后,就不应该被释放,因为还有B在用着它,也不能被线程N 重用。
所以一般使用 险象指针(hazard pointers,HP)来阻止释放还被其他线程使用的指针。 将来crossbeam-epoch也打算支持险象指针。
所以作者基于这种思想实现了一个crate: lockfree
该库提供了一些无锁数据结构,以及提供API为每个线程都增加一个删除队列,解决ABA问题
「系列文章」为Eurobot创建机器人:第1部分
作者是为了参加某个机器人比赛,基于Raspberry PI 3,用Rust编写机器人固件
Smithy进度更新:我如何将WebAssembly 打包尺寸减少90%
Smithy,一个用Rust编写的Web开发框架,可以编译为WebAssembly,它的目标是让你使用惯用的Rust来编写前端代码。
开始准备alpha版本了。新版本使用了wasm-bindgen提供的js-sys和web-sys
使用Runner来运行小的Rust代码片段
$ cat print.rs println!("Hello, World!");
$ runner print.rs Hello, World!
runner对代码片段默认会有main函数包装:
// file.rs let mut f = File::open("file.rs")?; let mut buf = String::new(); f.read_to_string(&mut buf)?; println!("{}",buf);
还可以直接运行代码:
$ runner -i "(0..4).map(|n| 2*n)" 0 2 4 6
总之,就像是一个方便的终端版的playground
[WIP]Rust编写的模块化系统监视器
先关注下
评论区
写评论还没有评论