pull/175/head
chenxuuu 3 years ago
commit 26723caba4
No known key found for this signature in database
GPG Key ID: B1A462D4300513F3

@ -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)
@ -146,7 +145,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 +168,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)

@ -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<u32> = 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<usize> = 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<usize> = RefCell::new(0);
}
struct Bar {
foo: &'static LocalKey<RefCell<usize>>,
}
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要保持尽量小的运行时。

@ -1,6 +1,6 @@
# 引用与借用
上节中提到,如果仅仅是支持所有权转移,那会让程序变得复杂。 能否像其它编程语言一样,使用某个变量的指针或者引用呢?答案是有的
上节中提到,如果仅仅支持通过转移所有权的方式获取一个值,那会让程序变得复杂。 Rust能否像其它编程语言一样使用某个变量的指针或者引用呢答案是可以
Rust通过`借用(Borrowing)`这个概念来达成上述的目的: **获取变量的引用,称之为借用(borrowing)**。正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来,当使用完毕后,也必须要物归原主.

@ -1 +1,3 @@
# Option枚举
https://www.reddit.com/r/learnrust/comments/rz34ht/where_does_the_data_go_if_you_replace_some_with/

Loading…
Cancel
Save