http://www.7klian.com

Rust如何担保多线程应用措施中的安详性

利用Atomic Acquire/Release,我们可以实现一个全成果的spinlock,它掩护代码的某个区域不受多个线程的并发会见:
另一方面,竞争条件是语义错误。譬喻我们可以错误地假设一个事件老是在另一个事件之前产生。竞争条件冲破了域逻辑稳定性,凡是是不正确同步或缺少同步的符号。Rust无法使我们免于犯语义级此外错误。实际上,设计这样的语言是不行行的。死锁和活锁也是语义错误,是域逻辑稳定的功效,譬喻我们假设锁A老是在持有锁B的同时产生,可是我们在代码中的某处实现了导致死锁的另一种方法。因此我们独一要谈论的是Rust如何防备数据竞争。
    let y = y.clone();
}
        y.store(true, Ordering::Release);
{
原子性是初级同步原语,它通过限制操纵顺序使我们具有因果干系。这些原语必需是由处理惩罚器级,因为除了限制编译器之外,我们还但愿从高速缓存级的重排序和其他方面限制处理惩罚器。 原子性提供两个担保:
}
超线程是指处理惩罚器将每个物理内核虚拟地拆分为两个虚拟内核时,可以实现更好的负载分派。操纵系统线程由操纵系统内部建设和打点,个中每个线程执行本身的代码,并轮番在虚拟内核上运行。大大都操纵系统实际上使线程数不受限制,可是不幸的是,启动它们是昂贵的,因为它需要分派仓库。绿色线程由用户软件实现,它们在OS线程之上运行。 绿色线程的利益是:纵然在没有OS线程支持的情况中,它们也可以事情;它们比通例线程旋转起来快得多。
如前所述,,Acquire/Release是硬件级操纵,因为它们为硬件生成非凡指令。可以通过以下方法利用,Acquire/Release:
Ordering::Relaxed

由于操纵系统大概比虚拟焦点具有更多的线程,因此大概尚有另一个线程正在期待调治。 yield_now汇报操纵系统,它可以实验在该虚拟内核上运行另一个线程,而第一个线程期待其锁定。
result += 1;
在上一节中,我们接头了原子,它们是在硬件级别运行的初级同步原语。 Mutex和RwLock是高级同步原语,它们在OS级别上运行。
在Rust中,当您听到人们谈论并行性和并发性时,它主要是关于框架的,因为Rust作为一种语言不支持任何特定的并行性或并发性抽象,而是提供了最低限度的要求,譬喻尺度线程和多个同步原语。这是我们将在本文中探讨的最低限度的内容。
在SeqCst操纵之前/之后产生的所有原子操纵在所有线程上都保持在其之前/之后。普通的非原子读和写操纵大概会在原子读取中向下移动,可能在原子写入中向上移动。
硬件线程,又名超线程;
    std::thread::yield_now();
    thread::spawn(move || {
}
impl<T: ?Sized + Send>        Send for RwLock<T>
locked.store(false, Ordering::Release);
不幸的是,锈已经去除了绿色的线程,此刻只答允裸操纵系统线程。 这样做是因为绿色线程不是零本钱的抽象,这是Rust区别于其他语言的根基法则。
    }).join.unwrap();
    thread::spawn(move || {
获取/释放(Acquire/Release)
        println!(“{}”, x);
在rustseqcst中,它会发出一个防备不需要的从头排序的内存屏障。 不幸的是,SeqCst比纯Acquire / Rease更昂贵,然而,在全局范畴内仍然可以忽略不计,因此强烈发起尽大概利用seqcst。
acquire只能用于加载操纵,而release只能用于存储操纵。Acquire/Release具有以下法则:
    });
        if b { assert!(a); }
您险些老是会利用SeqCst,它应用最强的约束而且最容易推论。relaxed应用了最弱的约束,而且很是不直观,因此除非您正在开拓初级此外高机能代码,不然应该远离它。就认知巨大性而言,Acquire/Release是中间点,但在SEQCST上,你险些永远不会喜欢它。然而,领略Acquire/Release对付领略高级同步原语(如Mutex和RwLock)很是有辅佐。
在上面的代码中,我们利用了线程,而没有表明它们是什么。 凡是,,人们将线程称为三件事:
Mutex是一个同步生成器。在下一部门中,我们将看到什么是发送和同步特征,并将再次会见Mutex和RwLock;

Rust的大部门开拓人员都有C/C++配景,这让开拓者很容易过渡到Rust并行性,因为它长短常相似。可是对付很多来自其他开拓语言的人来说,这是一个挑战。在本文中,我们将慢慢先容尺度的Rust并行性东西及其背后的念头。在一开始,这将需要对硬件举办深入研究,然后是对诸如原子之类的初级东西的表明,最后是对诸如Mutex之类的高级东西的表明。最后,我们将说明Rust如何担保多线程应用措施中的安详性。

我们甚至不需要查察整个条理布局就可以领略为什么如此坚苦。让我们删除所有缓存并仅思量写缓冲区。写缓冲区对付处理惩罚器的机能绝对必不行少,因为对内存的写操纵很是昂贵,我们但愿尽大概多地对其举办批处理惩罚。

请留意,除了上述限制之外,“Acquire/Release”操纵还具有别的一个恍惚法则,可以防备自旋锁(如上面的锁)受到滋扰。 Rust从C11内存模子担任了它。
let y = Arc::new(AtomicBool::new(false));
    let x = 42;
    });
// Do important stuff that only one thread can execute at a time.
初级原语(Low-Level Primitives)
发送(send)和同步(Sync)旨在防备数据争用。 发送(send)和同步(Sync)是自动派生的,不安详和标志特征。
在上一篇文章中,我们相识了Rust如何通过借用法则和保留期来提供单线程
安详性。 发送和同步特征将这种安详性扩展到多线程应用措施中。
fn main() {
不幸的是,在很多环境下,Acquire/Release仍然存在很难的争论。请看以下代码:

绿色线程;
· 我们可以对它们执行读/写操纵,而不必担忧读或写被粉碎。
因此,在以下环境下,假如线程A执行左侧代码,则线程B和C可以在内部举办所有操纵并举办互换,如下所示。然而,在acquire之前,他们看不到a=“bye”的产生。

x.store(result, Ordering::Release); // The value is now 1.
impl<T: ?Sized + Send + Sync> Sync for RwLock<T>
顺序一致性
关于Rust安详性,最重要的相识是它只能防备数据争用,而不能阻止其他任何工作。当一个线程写入内存区域,而另一个线程从该区域读取或写入该区域时,就会产生数据争用,这会导致读写操纵间断。数据争用尤其令人讨厌,因为它们大概导致未界说的行为。它们的原因长短常明晰的,因此可以像Rust一样自动检测到或在语言级别上阻止它。

在这段代码中,两条动静都有大概被打印,这意味着线程c和线程d对最先产生的事件有纷歧致的视图。换句话说,利用acquire/release没有全局操纵顺序。acquire/release只是建设通报的因果干系,seqcst成立了一个全局操纵顺序。假如我们将上述获取/宣布替换为seqcst,则最多将打印一条动静。矫正式地说,seqcst遵循以下法则:

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

相关文章阅读