10 个不稳定版本 (3 个重大更改)
使用旧的 Rust 2015
0.4.0 | 2018年3月29日 |
---|---|
0.3.0 | 2017年7月21日 |
0.2.4 | 2017年3月14日 |
0.2.2 | 2016年11月11日 |
0.1.1 | 2015年9月23日 |
#335 in 并发
在 2 crates 中使用
66KB
788 行
软件事务内存 
此库实现了 软件事务内存,通常缩写为 STM。
它设计得非常接近 Haskell 的 STM 库。阅读 Simon Marlow 的 Haskell 并行和并发编程 获取更多信息。特别是关于 性能 的章节也非常重要,这对于在 Rust 中使用 STM 也同样重要。
与锁相比,两个线程安全操作的顺序组合不再是线程安全的,因为其他线程可能会在这些操作之间进行干扰。使用第三个锁来保护这两个操作可能会导致死锁或竞态条件等常见的错误来源。
与锁不同,软件事务内存是可组合的。它通常通过在日志中编写所有读取和写入操作来实现。当操作完成并且所有使用的 TVar
都是连续的时,写入作为一个单一原子操作提交。否则,计算会重复。这可能导致饥饿,但避免了常见的错误来源。
在 STM 中恐慌不会污染 TVar
。STM 通过永远不在恐慌时提交来确保一致性。
用法
您应仅使用可安全使用的函数。
除了从本库中的原子变量之外,不要有副作用。特别是在软件事务内存内部使用互斥锁或其他阻塞机制是非常危险的。
您可以通过调用 atomically
来运行顶级原子操作。
use stm::atomically;
atomically(|trans| {
// some action
// return value as `Result`, for example
Ok(42)
});
不要嵌套调用 atomically
。
要在另一个操作内部运行原子操作,传递对 Transaction
的可变引用,并在结果上调用 try!
或使用 ?
。您不应自行处理错误,因为这会破坏一致性。
use stm::{atomically, TVar};
let var = TVar::new(0);
let x = atomically(|trans| {
var.write(trans, 42)?; // Pass failure to parent.
var.read(trans) // Return the value saved in var.
});
println!("var = {}", x);
STM 安全性
在 Rust 考虑的范围内,软件事务内存是完全安全的。尽管如此,在处理软件事务内存时,您仍需遵守多个规则。
- 不要运行具有副作用(尤其是I/O代码)的代码,因为STM在检测到不一致的状态时会重复计算。如果必须,请返回一个闭包。
- 除非你绝对清楚自己在做什么,否则不要自己处理错误类型。使用
Transaction::or
来组合替代路径。始终调用try!
或?
,永远不要忽略StmResult
。 - 不要在另一个内部运行
atomically
。因为atomically
是设计用来有副作用的,所以它会破坏STM的假设。嵌套调用在运行时被检测,并以panic方式处理。当你在函数内部使用STM时,请通过将&mut Transaction
作为参数并返回StmResult<T>
来在公共接口中表达它。调用者可以安全地将它组合成更大的块。 - 不要混合锁和事务。你的代码很容易死锁或在不规则的情况下变慢。
- 不要使用内部可变性来改变
TVar
的内容。
速度
通常尽量保持你的原子块尽可能小,因为花费的时间越多,与其他线程发生冲突的可能性就越大。对于STM来说,读取 TVar
很慢,因为它需要每次都查找日志。每个使用的 TVar
都会增加冲突的机会。因此,你应该将访问变量的数量保持在最低限度。
许可证
许可如下之一
- Apache许可证,版本2.0(LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT 或 https://open-source.org.cn/licenses/MIT)
任选其一。
贡献
除非你明确表示,否则,根据Apache-2.0许可证的定义,你提交的任何旨在包含在作品中的有意贡献,都应按上述方式双许可,不附加任何额外条款或条件。
依赖项
~1MB
~15K SLoC