From ebf666a3e66549c6028234208ea0527040888c49 Mon Sep 17 00:00:00 2001 From: sunface Date: Tue, 11 Jan 2022 12:15:55 +0800 Subject: [PATCH 1/2] update toc --- book/contents/SUMMARY.md | 69 +++++++++---------- book/contents/compiler/optimization/option.md | 2 + 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/book/contents/SUMMARY.md b/book/contents/SUMMARY.md index b7e3fcdb..9ada356a 100644 --- a/book/contents/SUMMARY.md +++ b/book/contents/SUMMARY.md @@ -146,7 +146,7 @@ - [简化错误处理 todo](errors/simplify.md) - [自定义错误 todo](errors/user-define.md) - [让错误输出更优雅 todo](errors/pretty-format.md) - - [会导致panic的代码](errors/panic-codes.md) + - [会导致panic的代码 todo](errors/panic-codes.md) - [Cargo详解 todo](cargo/intro.md) @@ -169,65 +169,64 @@ - [持续集成 todo](test/ci.md) - [常见特征解析 todo](traits/intro.md) - - [类型转换From/Into](traits/from-into.md) - - [AsRef, AsMut](traits/as-ref-as-mut.md) - - [Borrow, BorrowMut, ToOwned](traits/borrow-family.md) - - [Deref和引用隐式转换](traits/deref.md) - - [写时拷贝Cow](traits/cow.md) - - [Eq](traits/eq.md) + - [类型转换From/Into todo](traits/from-into.md) + - [AsRef, AsMut todo](traits/as-ref-as-mut.md) + - [Borrow, BorrowMut, ToOwned todo](traits/borrow-family.md) + - [Deref和引用隐式转换 todo](traits/deref.md) + - [写时拷贝Cow todo](traits/cow.md) + - [Eq todo](traits/eq.md) - [深入内存 todo](memory/intro.md) - - [指针和引用(todo)](memory/pointer-ref.md) - - [未初始化内存(todo)](memory/uninit.md) - - [内存分配(todo)](memory/allocation.md) - - [内存布局(todo)](memory/layout.md) - - [虚拟内存(todo)](memory/virtual.md) + - [指针和引用 todo](memory/pointer-ref.md) + - [未初始化内存 todo](memory/uninit.md) + - [内存分配 todo](memory/allocation.md) + - [内存布局 todo](memory/layout.md) + - [虚拟内存 todo](memory/virtual.md) - [Web应用开发 todo](web/intro.md) - [编解码与序列化 todo](web/serialization.md) - [面向对象 todo](object-oriented/intro.md) - - [为何OO(todo)](object-oriented/characteristics.md) - - [特征对象](object-oriented/trait-object.md) - - [设计模式](object-oriented/design-pattern.md) + - [为何OO todo](object-oriented/characteristics.md) + - [设计模式 todo](object-oriented/design-pattern.md) - [不安全Rust todo](unsafe/intro.md) - - [原生指针(todo)](unsafe/raw-pointer.md) - - [修改全局变量](unsafe/modify-global-var.md) - - [FFI外部语言用](unsafe/ffi.md) - - [那些会导致UB的代码](unsafe/ub.md) + - [原生指针 todo](unsafe/raw-pointer.md) + - [修改全局变量 todo](unsafe/modify-global-var.md) + - [FFI外部语言用 todo](unsafe/ffi.md) + - [那些会导致UB的代码 todo](unsafe/ub.md) - [宏编程 todo](macro/intro.md) - [过程宏(todo)](macro/procedure-macro.md) -- [性能调优 todo](performance/intro.md) +- [性能调优 doing](performance/intro.md) - [深入理解move](performance/deep-into-move.md) - - [糟糕的提前优化](performance/early-optimise.md) - - [Clone和Copy](performance/clone-copy.md) + - [糟糕的提前优化 todo](performance/early-optimise.md) + - [Clone和Copy todo](performance/clone-copy.md) - [Benchmark性能测试(todo)](performance/benchmark.md) - [减少Runtime check(todo)](performance/runtime-check.md) - - [CPU缓存性能优化](performance/cpu-cache.md) - - [计算性能优化](performance/calculate.md) - - [堆和栈](performance/heap-stack.md) - - [常用性能测试工具](performance/tools.md) + - [CPU缓存性能优化 todo](performance/cpu-cache.md) + - [计算性能优化 todo](performance/calculate.md) + - [堆和栈 todo](performance/heap-stack.md) + - [常用性能测试工具 todo](performance/tools.md) - [编译器 todo](compiler/intro.md) - [常见属性标记 todo](compiler/attributes.md) - [提升编译速度 todo](compiler/speed-up.md) - - [编译器优化](compiler/optimization/intro.md) - - [Option枚举](compiler/optimization/option.md) + - [编译器优化 todo](compiler/optimization/intro.md) + - [Option枚举 todo](compiler/optimization/option.md) - [日志和监控 todo](monitor/intro.md) - - [日志](monitor/log.md) - - [可观测性](monitor/observability.md) - - [监控(APM)](monitor/apm.md) + - [日志 todo](monitor/log.md) + - [可观测性 todo](monitor/observability.md) + - [监控(APM) todo](monitor/apm.md) - [标准库解析 todo](std/intro.md) - - [如何寻找你想要的内容](std/search.md) - - [Vector常用方法](std/vector.md) - - [HashMap](std/hashmap.md) - - [Iterator常用方法](std/iterator.md) + - [如何寻找你想要的内容 todo](std/search.md) + - [Vector常用方法 todo](std/vector.md) + - [HashMap todo](std/hashmap.md) + - [Iterator常用方法 todo](std/iterator.md) - [常用三方库 todo](libraries/intro.md) - [JSON](libraries/json/intro.md) diff --git a/book/contents/compiler/optimization/option.md b/book/contents/compiler/optimization/option.md index f65ae257..36c0f2e6 100644 --- a/book/contents/compiler/optimization/option.md +++ b/book/contents/compiler/optimization/option.md @@ -1 +1,3 @@ # Option枚举 + +https://www.reddit.com/r/learnrust/comments/rz34ht/where_does_the_data_go_if_you_replace_some_with/ From f9f56063f474d0c7897c132405f78b61268e8768 Mon Sep 17 00:00:00 2001 From: sunface Date: Tue, 11 Jan 2022 14:41:15 +0800 Subject: [PATCH 2/2] =?UTF-8?q?upadte=E5=A4=9A=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/contents/SUMMARY.md | 3 +- .../message-passing.md | 2 +- .../concurrency-with-threads/thread.md | 213 ++++++++++++++++++ 3 files changed, 215 insertions(+), 3 deletions(-) diff --git a/book/contents/SUMMARY.md b/book/contents/SUMMARY.md index 9ada356a..87e0c857 100644 --- a/book/contents/SUMMARY.md +++ b/book/contents/SUMMARY.md @@ -76,8 +76,7 @@ - [多线程并发编程 doing](advance/concurrency-with-threads/intro.md) - [并发和并行](advance/concurrency-with-threads/concurrency-parallelism.md) - [使用多线程](advance/concurrency-with-threads/thread.md) - - [消息传递 todo](advance/concurrency-with-threads/message-passing.md) - - [数据共享Mutex、Rwlock todo](advance/concurrency-with-threads/ref-counter-lock.md) + - [消息与锁](advance/concurrency-with-threads/message-passing.md) - [Send、Sync todo](advance/multi-threads/send-sync.md) - [一个综合例子](advance/multi-threads/example.md) - [async/await并发编程 todo](advance/async/intro.md) diff --git a/book/contents/advance/concurrency-with-threads/message-passing.md b/book/contents/advance/concurrency-with-threads/message-passing.md index 01fa0785..26cabdb3 100644 --- a/book/contents/advance/concurrency-with-threads/message-passing.md +++ b/book/contents/advance/concurrency-with-threads/message-passing.md @@ -1 +1 @@ -# 消息传递(todo) +# 消息与锁 diff --git a/book/contents/advance/concurrency-with-threads/thread.md b/book/contents/advance/concurrency-with-threads/thread.md index 5d91d258..8ff0f1b5 100644 --- a/book/contents/advance/concurrency-with-threads/thread.md +++ b/book/contents/advance/concurrency-with-threads/thread.md @@ -261,6 +261,219 @@ for handle in handles { 总之,多线程的开销往往是在锁、数据竞争、缓存失效上,这些限制了现代化软件系统随着CPU核心的增多性能也线性增加的野心。 +## 线程屏障(Barrier) +在Rust中,可以使用`Barrier`让多个线程都执行到某个点后,才继续一起往后执行: +```rust +use std::sync::{Arc, Barrier}; +use std::thread; + +fn main() { + let mut handles = Vec::with_capacity(6); + let barrier = Arc::new(Barrier::new(6)); + + for _ in 0..6 { + let b = barrier.clone(); + handles.push(thread::spawn(move|| { + println!("before wait"); + b.wait(); + println!("after wait"); + })); + } + + for handle in handles { + handle.join().unwrap(); + } +} +``` + +上面代码,我们在线程打印出`before wait`后增加了一个屏障,目的就是等所有的线程都打印出**before wait**后,各个线程再继续执行: +```console +before wait +before wait +before wait +before wait +before wait +before wait +after wait +after wait +after wait +after wait +after wait +after wait +``` + +## 线程局部变量(Thread Loval Variable) +对于多线程编程,线程局部变量在一些场景下非常有用,而Rust通过标准库和三方库对此进行了支持。 + +#### 标准库thread_local +使用`thread_local`宏可以初始化线程局部变量,然后在线程内部使用该变量的`with`方法获取变量值: +```rust +use std::cell::RefCell; +use std::thread; + +thread_local!(static FOO: RefCell = RefCell::new(1)); + +FOO.with(|f| { + assert_eq!(*f.borrow(), 1); + *f.borrow_mut() = 2; +}); + +// 每个线程开始时都会拿到线程局部变量的FOO的初始值 +let t = thread::spawn(move|| { + FOO.with(|f| { + assert_eq!(*f.borrow(), 1); + *f.borrow_mut() = 3; + }); +}); + +// 等待线程完成 +t.join().unwrap(); + +// 尽管子线程中修改为了3,我们在这里依然拥有main线程中的局部值:2 +FOO.with(|f| { + assert_eq!(*f.borrow(), 2); +}); +``` + +上面代码中,`FOO`即是我们创建的**线程局部变量**,每个新的线程访问它时,都会使用它的初始值作为开始,各个线程中的`FOO`值彼此互不干扰。 + +可以注意到,线程中对`FOO`的使用是通过借用的方式,但是若我们需要每个线程独自获取它的拷贝,最后进行汇总,就有些强人所难了。 + +你还可以在结构体中使用线程局部变量: +```rust +use std::cell::RefCell; + +struct Foo; +impl Foo { + thread_local! { + static FOO: RefCell = RefCell::new(0); + } +} + +fn main() { + Foo::FOO.with(|x| println!("{:?}", x)); +} +``` + +或者通过引用的方式使用它: +```rust +use std::cell::RefCell; +use std::thread::LocalKey; + +thread_local! { + static FOO: RefCell = RefCell::new(0); +} +struct Bar { + foo: &'static LocalKey>, +} +impl Bar { + fn constructor() -> Self { + Self { + foo: &FOO, + } + } +} +``` + +#### 三方库thread-local +除了标准库外,一位大神还开发了[thread-local](https://github.com/Amanieu/thread_local-rs)库,它允许每个线程持有值的独立拷贝: +```rust +use thread_local::ThreadLocal; +use std::sync::Arc; +use std::cell::Cell; +use std::thread; + +let tls = Arc::new(ThreadLocal::new()); + +// 创建多个线程 +for _ in 0..5 { + let tls2 = tls.clone(); + thread::spawn(move || { + // 将计数器加1 + let cell = tls2.get_or(|| Cell::new(0)); + cell.set(cell.get() + 1); + }).join().unwrap(); +} + +// 一旦所有子线程结束,收集它们的线程局部变量中的计数器值,然后进行求和 +let tls = Arc::try_unwrap(tls).unwrap(); +let total = tls.into_iter().fold(0, |x, y| x + y.get()); + +// 和为5 +assert_eq!(total, 5); +``` + +该库不仅仅使用了值的拷贝,而且还能自动把多个拷贝汇总到一个迭代器中,最后进行求和,非常好用。 + + +## 用条件控制线程的挂起和执行 +条件变量(Condition Variables)经常和`Mutex`一起使用,可以让线程挂起,直到某个条件发生后再继续执行: +```rust +use std::thread; +use std::sync::{Arc, Mutex, Condvar}; + +fn main() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + thread::spawn(move|| { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + println!("changing started"); + *started = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*pair; + let mut started = lock.lock().unwrap(); + while !*started { + started = cvar.wait(started).unwrap(); + } + + println!("started changed"); +} +``` + +上述代码流程如下: + +1. `main`线程首先进入`while`循环,并释放了锁`started`,然后开始挂起等待子线程的通知 +2. 子线程获取到锁,并将其修改为true, 然后调用条件的方法来通知主线程继续执行:`cvar.notify_one` + +## 只被调用一次的函数 +有时,我们会需要某个函数在多线程环境下只被调用一次,例如初始化全局变量,无论是哪个线程先调用函数来初始化,都会保证全局变量只会被初始化一次,随后的其它线程调用就会忽略该函数: +```rust +use std::thread; +use std::sync::{Once, ONCE_INIT}; + +static mut VAL: usize = 0; +static INIT: Once = ONCE_INIT; + +fn main() { + let handle1 = thread::spawn(move || { + INIT.call_once(|| { + unsafe { + VAL = 1; + } + }); + }); + + let handle2 = thread::spawn(move || { + INIT.call_once(|| { + unsafe { + VAL = 2; + } + }); + }); + + handle1.join().unwrap(); + handle2.join().unwrap(); + + println!("{}", unsafe { VAL }); +} +``` + +代码运行的结果取决于哪个线程先调用`INIT.call_once`(虽然代码具有先后顺序,但是线程的初始化顺序并无法被保证!因为线程初始化是异步的,且耗时较久),若`handle1`先,则输出`1`,否则输出`2`。 + ## 总结 [Rust的线程模型](./intro.md)是`1:1`模型,因为Rust要保持尽量小的运行时。