33|并发处理(上):从atomics到Channel,Rust都提供了什么工具?
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
Rust并发编程技术概览 Rust提供了丰富的工具来处理并发和异步编程,包括atomics、Mutex、Condvar、Channel和Actor model。并发是处理多个任务的能力,而并行则是同时执行多个任务的手段。在处理并发过程中,关键在于如何进行同步。本文介绍了自由竞争模式、map/reduce模式和DAG模式等几种常见的并发工作模式。这些模式可以组合处理复杂的并发场景。接下来的内容将重点讲解和深入介绍atomics、Mutex、Condvar、Channel和Actor model这五个概念。 文章首先介绍了基本的锁实现,然后指出了其存在的问题,包括多核情况下的竞争问题以及编译器和CPU的优化可能导致的乱序执行问题。为了解决这些问题,文章引入了Compare-and-swap操作,简称CAS,作为基础保证。通过使用CAS操作和Ordering参数,可以规避多核情况下的竞争问题,并处理编译器和CPU的优化相关问题。最后,文章提出了对获取锁的性能优化方法,进一步完善了锁的实现。 总的来说,本文通过介绍基本的锁实现和存在的问题,引入CAS操作和Ordering参数,以及性能优化方法,全面展示了Rust在处理并发和同步方面的丰富工具和技术特点。文章还提到了SpinLock和Mutex的区别,以及Semaphore的实现方式,为读者提供了深入了解并发编程的思路和方法。 通过本文,读者可以快速了解Rust在并发编程方面的丰富工具和技术特点,为进一步深入学习提供了基础和思路。
《陈天 · Rust 编程第一课》,新⼈⾸单¥68
全部留言(16)
- 最新
- 精选
- Ethan Liurust相比go在并发处理上 有什么优点和缺点?
作者回复: 文中已经讲了,Rust 的优点是所有方案都支持,缺点是你需要考虑合适的场景用合适的工具,另外 Rust channel 在某些情况下性能不如 golang;golang 的优点是简单,CSP 一招鲜,大部分场景 channel 都能很好适用,缺点是遇到 channel 不好解决的问题,或者效率不高的问题,就比较尴尬
2021-11-167 - D. D既然 Semaphore 是 Mutex 的推广,那么实现的思路应该有点类似。 参考老师文章中所说的 Mutex 的实现方法,实现 Semaphore 的一个思路是: 我们可以用一个 AtomicUsize 记录可用的 permits 的数量。在获取 permits 的时候,如果无法获取到足够的 permits,就把当前线程挂起,放入 Semaphore 的一个等待队列里。获取到 permits 的线程完成工作后退出临界区时,Semaphore 给等待队列发送信号,把队头的线程唤醒。 至于像图书馆那样的人数控制系统,tokio 的 Semaphore 文档中使用 Semaphore::acquire_owned 的例子可以说就是这种场景的模拟。
作者回复: 👍
2021-11-142 - Geek_b52974作業: use std::sync::Arc; use tokio::{sync::Semaphore, task::JoinHandle}; #[tokio::main] async fn main() { let library = Box::new(Library::new(3)); let mut tasks: Vec<JoinHandle<()>> = vec![]; for i in 0..10 { tasks.push(library.enter(move || println!("no: {}, borrow book", i))); } for task in tasks { task.await.unwrap(); } println!("{:?}", library.semaphore.available_permits()); } struct Library { semaphore: Arc<Semaphore>, } impl Library { fn new(capacity: usize) -> Self { Self { semaphore: Arc::new(Semaphore::new(capacity)), } } fn enter(&self, chore: impl Fn() + Send + 'static) -> JoinHandle<()> { let semaphore = self.semaphore.clone(); tokio::spawn(async move { // remove this you will get panic // semaphore.close(); println!("{} quota left", semaphore.available_permits()); let s = semaphore.acquire_owned().await.unwrap(); chore(); drop(s); }) } }
作者回复: 👍
2021-12-171 - 终生恻隐// [dependencies] // tokio = { version = "1", features = ["full"] } // chrono = "*" use tokio::sync::{Semaphore, TryAcquireError}; use std::sync::Arc; use std::thread; use std::time::Duration; use chrono::prelude::*; #[tokio::main] async fn main() { let semaphore = Arc::new(Semaphore::new(3)); let mut join_handles = Vec::new(); for c in 0..5 { let permit = semaphore.clone().acquire_owned().await.unwrap(); join_handles.push(tokio::spawn(async move { let local: DateTime<Local> = Local::now(); println!("count{} start time: {}", c+1, local); thread::sleep(Duration::new(10, 0)); drop(permit); })); } for handle in join_handles { handle.await.unwrap(); } }
作者回复: Duration 有 from_secs / from_millis 等方法,不必用 Duration::new,这个接口不直观。
2021-11-121 - Milittlecas是指令集指令提供的能力 一般语言封装的并发原语都是在这个基础上的
作者回复: 对
2022-01-13 - 爱学习的小迪感觉和java对应的功能,原理一致
作者回复: 👍
2022-01-10 - 核桃另外关于Ordering那几个的区别,个人写代码的时候,绝大部分时候,要么最简单的Relaxed,要么最严格的SeqCst,剩下的,不一定考虑那么多,除非特殊需要才去查一下区别,平时记不住的。。。
作者回复: 不分场合地使用 Relaxed,会导致不一致的更新;不分场合地使用 SeqCst,会导致性能问题。
2021-12-05 - 核桃理解一下,对于所谓的指令原子操作,在计算机底层里面,没记错就是中断总线关闭吧。 然后那段优化的代码,就是拿不到锁先while空转一下,这里就有点像java的自旋锁的概念那样。 当然这里为什么空转性能会比spin好,从系统的角度理解,空转没有调用系统调用,那么就没有太多的进程或者线程切换,而切换这个是涉及上下文变更的。不管进程还是线程,上下文切换的时候,其实五大结构都是要考虑的,例如信号量那些是否要保存起来等等,那样代码自然会大很多了。 不知道这样理解是否对哈。
作者回复: 不是。atomic operation 是硬件提供的无法被中断打断的指令,并不是去关闭中断。 这里我们用 atomic 实现的 mutex,就是一个 spinlock。
2021-12-05 - Geek_b52974compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) 想问一下第三个参数 为何不是 acqRel,这样其他线程会知道吗?
作者回复: 你可以详细看文档:https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.compare_exchange
2021-11-16 - 罗杰CAS 的原理挺绕的,需要好好的消化,最近也在看“Go 并发实战课”,有一些互通的地方。
作者回复: 嗯
2021-11-12