2017-03-18 21:39:27 +08:00
<!DOCTYPE HTML>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< title > 共享状态 - Rust 程序设计语言 简体中文版< / title >
< meta content = "text/html; charset=utf-8" http-equiv = "Content-Type" >
< meta name = "description" content = "Rust 程序设计语言 简体中文版" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< base href = "" >
< link rel = "stylesheet" href = "book.css" >
< link href = 'https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel = 'stylesheet' type = 'text/css' >
< link rel = "shortcut icon" href = "favicon.png" >
<!-- Font Awesome -->
< link rel = "stylesheet" href = "https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" >
< link rel = "stylesheet" href = "highlight.css" >
< link rel = "stylesheet" href = "tomorrow-night.css" >
<!-- MathJax -->
< script type = "text/javascript" src = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" > < / script >
<!-- Fetch JQuery from CDN but have a local fallback -->
< script src = "https://code.jquery.com/jquery-2.1.4.min.js" > < / script >
< script >
if (typeof jQuery == 'undefined') {
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
}
< / script >
< / head >
< body class = "light" >
<!-- Set the theme before any content is loaded, prevents flash -->
< script type = "text/javascript" >
var theme = localStorage.getItem('theme');
if (theme == null) { theme = 'light'; }
$('body').removeClass().addClass(theme);
< / script >
<!-- Hide / unhide sidebar before it is displayed -->
< script type = "text/javascript" >
var sidebar = localStorage.getItem('sidebar');
if (sidebar === "hidden") { $("html").addClass("sidebar-hidden") }
else if (sidebar === "visible") { $("html").addClass("sidebar-visible") }
< / script >
< div id = "sidebar" class = "sidebar" >
2017-04-18 14:55:09 +08:00
< ul class = "chapter" > < li > < a href = "ch01-00-introduction.html" > < strong > 1.< / strong > 介绍< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch01-01-installation.html" > < strong > 1.1.< / strong > 安装< / a > < / li > < li > < a href = "ch01-02-hello-world.html" > < strong > 1.2.< / strong > Hello, World!< / a > < / li > < / ul > < / li > < li > < a href = "ch02-00-guessing-game-tutorial.html" > < strong > 2.< / strong > 猜猜看教程< / a > < / li > < li > < a href = "ch03-00-common-programming-concepts.html" > < strong > 3.< / strong > 通用编程概念< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch03-01-variables-and-mutability.html" > < strong > 3.1.< / strong > 变量和可变性< / a > < / li > < li > < a href = "ch03-02-data-types.html" > < strong > 3.2.< / strong > 数据类型< / a > < / li > < li > < a href = "ch03-03-how-functions-work.html" > < strong > 3.3.< / strong > 函数如何工作< / a > < / li > < li > < a href = "ch03-04-comments.html" > < strong > 3.4.< / strong > 注释< / a > < / li > < li > < a href = "ch03-05-control-flow.html" > < strong > 3.5.< / strong > 控制流< / a > < / li > < / ul > < / li > < li > < a href = "ch04-00-understanding-ownership.html" > < strong > 4.< / strong > 认识所有权< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch04-01-what-is-ownership.html" > < strong > 4.1.< / strong > 什么是所有权< / a > < / li > < li > < a href = "ch04-02-references-and-borrowing.html" > < strong > 4.2.< / strong > 引用 & 借用< / a > < / li > < li > < a href = "ch04-03-slices.html" > < strong > 4.3.< / strong > Slices< / a > < / li > < / ul > < / li > < li > < a href = "ch05-00-structs.html" > < strong > 5.< / strong > 结构体< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch05-01-method-syntax.html" > < strong > 5.1.< / strong > 方法语法< / a > < / li > < / ul > < / li > < li > < a href = "ch06-00-enums.html" > < strong > 6.< / strong > 枚举和模式匹配< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch06-01-defining-an-enum.html" > < strong > 6.1.< / strong > 定义枚举< / a > < / li > < li > < a href = "ch06-02-match.html" > < strong > 6.2.< / strong > < code > match< / code > 控制流运算符< / a > < / li > < li > < a href = "ch06-03-if-let.html" > < strong > 6.3.< / strong > < code > if let< / code > 简单控制流< / a > < / li > < / ul > < / li > < li > < a href = "ch07-00-modules.html" > < strong > 7.< / strong > 模块< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch07-01-mod-and-the-filesystem.html" > < strong > 7.1.< / strong > < code > mod< / code > 和文件系统< / a > < / li > < li > < a href = "ch07-02-controlling-visibility-with-pub.html" > < strong > 7.2.< / strong > 使用< code > pub< / code > 控制可见性< / a > < / li > < li > < a href = "ch07-03-importing-names-with-use.html" > < strong > 7.3.< / strong > 使用< code > use< / code > 导入命名< / a > < / li > < / ul > < / li > < li > < a href = "ch08-00-common-collections.html" > < strong > 8.< / strong > 通用集合类型< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch08-01-vectors.html" > < strong > 8.1.< / strong > vector< / a > < / li > < li > < a href = "ch08-02-strings.html" > < strong > 8.2.< / strong > 字符串< / a > < / li > < li > < a href = "ch08-03-hash-maps.html" > < strong > 8.3.< / strong > 哈希 map< / a > < / li > < / ul > < / li > < li > < a href = "ch09-00-error-handling.html" > < strong > 9.< / strong > 错误处理< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch09-01-unrecoverable-errors-with-panic.html" > < strong > 9.1.< / strong > < code > panic!< / code > 与不可恢复的错误< / a > < / li > < li > < a href = "ch09-02-recoverable-errors-with-result.html" > < strong > 9.2.< / strong > < code > Result< / code > 与可恢复的错误< / a > < / li > < li > < a href = "ch09-03-to-panic-or-not-to-panic.html" > < strong > 9.3.< / strong > < code > panic!< / code > 还是不< code > panic!< / code > < / a > < / li > < / ul > < / li > < li > < a href = "ch10-00-generics.html" > < strong > 10.< / strong > 泛型、trait 和生命周期< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch10-01-syntax.html" > < strong > 10.1.< / strong > 泛型数据类型< / a > < / li > < li > < a href = "ch10-02-traits.html" > < strong > 10.2.< / strong > trait: 定义共享的行为< / a > < / li > < li > < a href = "ch10-03-lifetime-syntax.html" > < strong > 10.3.< / strong > 生命周期与引用有效性< / a > < / li > < / ul > < / li > < li > < a href = "ch11-00-testing.html" > < strong > 11.< / strong > 测试< / a > < / li > < li > < ul class = "section" > < li > < a href = "ch11-01-writing-tests.html" > < strong > 11.1.< / strong > 编写测试< / a > < / li > < li > < a href = "ch11-02-running-tests.html" > < strong > 11.2.< / strong > 运行测试< / a > < / li > < li > < a href = "ch11-03-test-organization.html" > < strong > 11.3.< / strong > 测试的组织结构< / a > < / li > < / ul > < / li > < li > < a href = "ch12-00-an-io-project.h
2017-03-18 21:39:27 +08:00
< / div >
< div id = "page-wrapper" class = "page-wrapper" >
< div class = "page" >
< div id = "menu-bar" class = "menu-bar" >
< div class = "left-buttons" >
< i id = "sidebar-toggle" class = "fa fa-bars" > < / i >
< i id = "theme-toggle" class = "fa fa-paint-brush" > < / i >
< / div >
< h1 class = "menu-title" > Rust 程序设计语言 简体中文版< / h1 >
< div class = "right-buttons" >
< i id = "print-button" class = "fa fa-print" title = "Print this book" > < / i >
< / div >
< / div >
< div id = "content" class = "content" >
2017-03-21 23:49:21 +08:00
< a class = "header" href = "#共享状态并发" name = "共享状态并发" > < h2 > 共享状态并发< / h2 > < / a >
< blockquote >
< p > < a href = "https://github.com/rust-lang/book/blob/master/second-edition/src/ch16-03-shared-state.md" > ch16-03-shared-state.md< / a >
< br >
commit 9df612e93e038b05fc959db393c15a5402033f47< / p >
< / blockquote >
< p > 虽然消息传递是一个很好的处理并发的方式,但并不是唯一的一个。再次考虑一下它的口号:< / p >
< blockquote >
< p > Do not communicate by sharing memory; instead, share memory by
communicating.< / p >
< p > 不要共享内存来通讯;而是要通讯来共享内存。< / p >
< / blockquote >
2017-05-15 08:27:11 +08:00
< p > 那么“共享内存来通讯”是怎样的呢?共享内存并发有点像多所有权:多个线程可以同时访问相同的内存位置。第十五章介绍了智能指针如何使得多所有权成为可能,然而这会增加额外的复杂性,因为需要以某种方式管理这些不同的所有者。< / p >
< p > 不过 Rust 的类型系统和所有权可以很好的帮助我们, 正确的管理它们。以共享内存中更常见的并发原语: 互斥器( mutexes) 为例, 让我们看看具体的情况。< / p >
2017-03-21 23:49:21 +08:00
< a class = "header" href = "#互斥器一次只允许一个线程访问数据" name = "互斥器一次只允许一个线程访问数据" > < h3 > 互斥器一次只允许一个线程访问数据< / h3 > < / a >
2017-05-15 08:27:11 +08:00
< p > < strong > 互斥器< / strong > ( < em > mutex< / em > ) 是一种用于共享内存的并发原语。它是“mutual exclusion”的缩写, 也就是说, 任意时间, 它只允许一个线程访问某些数据。互斥器以难以使用著称, 因为你不得不记住: < / p >
2017-03-21 23:49:21 +08:00
< ol >
2017-05-15 08:27:11 +08:00
< li > 在使用数据之前尝试获取锁。< / li >
< li > 处理完被互斥器所保护的数据之后,必须解锁数据,这样其他线程才能够获取锁。< / li >
2017-03-21 23:49:21 +08:00
< / ol >
2017-05-15 08:27:11 +08:00
< p > 现实中也有互斥器的例子,想象一下在一个会议中,只有一个麦克风。如果一个成员要发言,他必须请求使用麦克风。一旦得到了麦克风,他可以畅所欲言,然后将麦克风交给下一个希望讲话的成员。如果成员在没有麦克风的时候就开始叫喊,或者在其他成员发言结束之前就拿走麦克风,是很不合适的。如果这个共享的麦克风因为此类原因而出现问题,会议将无法正常进行。< / p >
< p > 正确的管理互斥器异常复杂,这也是许多人之所以热衷于通道的原因。然而,在 Rust 中,得益于类型系统和所有权,我们不会在锁和解锁上出错。< / p >
2017-03-21 23:49:21 +08:00
< a class = "header" href = "#mutext的-api" name = "mutext的-api" > < h3 > < code > Mutex< T> < / code > 的 API< / h3 > < / a >
2017-05-15 08:27:11 +08:00
< p > 让我们看看列表 16-12 中使用互斥器的例子,现在不涉及多线程:< / p >
2017-03-21 23:49:21 +08:00
< p > < span class = "filename" > Filename: src/main.rs< / span > < / p >
< pre > < code class = "language-rust" > use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!(" m = {:?}" , m);
}
< / code > < / pre >
< p > < span class = "caption" > Listing 16-12: Exploring the API of < code > Mutex< T> < / code > in a
single threaded context for simplicity< / span > < / p >
2017-05-15 08:27:11 +08:00
< p > 像很多类型一样,我们使用关联函数 < code > new< / code > 来创建一个 < code > Mutex< T> < / code > 。使用< code > lock< / code > 方法获取锁,以访问互斥器中的数据。这个调用会阻塞,直到我们拥有锁为止。如果另一个线程拥有锁,并且那个线程 panic 了,则这个调用会失败。类似于列表 16-6 那样,我们暂时使用 < code > unwrap()< / code > 进行错误处理,或者使用第九章中提及的更好的工具。< / p >
< p > 一旦获取了锁,就可以将返回值(在这里是< code > num< / code > )作为一个数据的可变引用使用了。观察 Rust 类型系统如何保证使用值之前必须获取锁:< code > Mutex< i32> < / code > 并不是一个< code > i32< / code > ,所以< strong > 必须< / strong > 获取锁才能使用这个< code > i32< / code > 值。我们是不会忘记这么做的,因为类型系统不允许。< / p >
< p > 你也许会怀疑,< code > Mutex< T> < / code > 是一个智能指针?是的!更准确的说,< code > lock< / code > 调用返回一个叫做< code > MutexGuard< / code > 的智能指针。类似我们在第十五章见过的智能指针,它实现了< code > Deref< / code > 来指向其内部数据。另外< code > MutexGuard< / code > 有一个用来释放锁的< code > Drop< / code > 实现。这样就不会忘记释放锁了。这在< code > MutexGuard< / code > 离开作用域时会自动发生,例如它发生于列表 16-12 中内部作用域的结尾。接着可以打印出互斥器的值并发现能够将其内部的< code > i32< / code > 改为 6。< / p >
2017-03-21 23:49:21 +08:00
< a class = "header" href = "#在线程间共享mutext" name = "在线程间共享mutext" > < h4 > 在线程间共享< code > Mutex< T> < / code > < / h4 > < / a >
2017-05-15 08:27:11 +08:00
< p > 现在让我们尝试使用< code > Mutex< T> < / code > 在多个线程间共享值。我们将启动十个线程,并在各个线程中对同一个计数器值加一,这样计数器将从 0 变为 10。注意, 接下来的几个例子会出现编译错误, 而我们将通过这些错误来学习如何使用
< code > Mutex< T> < / code > ,以及 Rust 又是如何辅助我们以确保正确。列表 16-13 是最开始的例子:< / p >
2017-03-21 23:49:21 +08:00
< p > < span class = "filename" > Filename: src/main.rs< / span > < / p >
< pre > < code class = "language-rust,ignore" > use std::sync::Mutex;
use std::thread;
fn main() {
let counter = Mutex::new(0);
let mut handles = vec![];
for _ in 0..10 {
let handle = thread::spawn(|| {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!(" Result: {}" , *counter.lock().unwrap());
}
< / code > < / pre >
< p > < span class = "caption" > Listing 16-13: The start of a program having 10 threads
each increment a counter guarded by a < code > Mutex< T> < / code > < / span > < / p >
2017-05-15 08:27:11 +08:00
< p > 这里创建了一个 < code > counter< / code > 变量来存放内含 < code > i32< / code > 的 < code > Mutex< T> < / code > ,类似列表 16-12 那样。接下来使用 range 创建了 10 个线程。使用了 < code > thread::spawn< / code > 并对所有线程使用了相同的闭包:他们每一个都将调用 < code > lock< / code > 方法来获取 < code > Mutex< T> < / code > 上的锁,接着将互斥器中的值加一。当一个线程结束执行,< code > num< / code > 会离开闭包作用域并释放锁,这样另一个线程就可以获取它了。< / p >
< p > 在主线程中,我们像列表 16-2 那样收集了所有的 join 句柄,调用它们的 < code > join< / code > 方法来确保所有线程都会结束。之后,主线程会获取锁并打印出程序的结果。< / p >
2017-03-21 23:49:21 +08:00
< p > 之前提示过这个例子不能编译,让我们看看为什么!< / p >
< pre > < code > error[E0373]: closure may outlive the current function, but it borrows
`counter`, which is owned by the current function
-->
|
9 | let handle = thread::spawn(|| {
| ^^ may outlive borrowed value `counter`
10 | let mut num = counter.lock().unwrap();
| ------- `counter` is borrowed here
|
help: to force the closure to take ownership of `counter` (and any other
referenced variables), use the `move` keyword, as shown:
| let handle = thread::spawn(move || {
< / code > < / pre >
2017-05-15 08:27:11 +08:00
< p > 这类似于列表 16-5 中解决了的问题。考虑到启动了多个线程, Rust 无法知道这些线程会运行多久,而在每一个线程尝试借用 < code > counter< / code > 时它是否仍然有效。帮助信息提醒了我们如何解决它:可以使用 < code > move< / code > 来给予每个线程其所有权。尝试在闭包上做一点改动:< / p >
2017-03-21 23:49:21 +08:00
< pre > < code class = "language-rust,ignore" > thread::spawn(move || {
< / code > < / pre >
2017-05-15 08:27:11 +08:00
< p > 再次编译。这回出现了一个不同的错误!< / p >
2017-03-21 23:49:21 +08:00
< pre > < code > error[E0382]: capture of moved value: `counter`
-->
|
9 | let handle = thread::spawn(move || {
| ------- value moved (into closure) here
10 | let mut num = counter.lock().unwrap();
| ^^^^^^^ value captured here after move
|
= note: move occurs because `counter` has type `std::sync::Mutex< i32> `,
which does not implement the `Copy` trait
error[E0382]: use of moved value: `counter`
-->
|
9 | let handle = thread::spawn(move || {
| ------- value moved (into closure) here
...
21 | println!(" Result: {}" , *counter.lock().unwrap());
| ^^^^^^^ value used here after move
|
= note: move occurs because `counter` has type `std::sync::Mutex< i32> `,
which does not implement the `Copy` trait
error: aborting due to 2 previous errors
< / code > < / pre >
2017-05-15 08:27:11 +08:00
< p > < code > move< / code > 并没有像列表 16-5 中那样解决问题。为什么呢?错误信息有点难懂,因为它表明 < code > counter< / code > 被移动进了闭包,接着它在调用 < code > lock< / code > 时被捕获。这似乎是我们希望的,然而不 被允许。< / p >
< p > 让我们推理一下。这次不再使用 < code > for< / code > 循环创建 10 个线程,只创建两个线程,看看会发生什么。将列表 16-13 中第一个< code > for< / code > 循环替换为如下代码:< / p >
2017-03-21 23:49:21 +08:00
< pre > < code class = "language-rust,ignore" > let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
let handle2 = thread::spawn(move || {
let mut num2 = counter.lock().unwrap();
*num2 += 1;
});
handles.push(handle2);
< / code > < / pre >
2017-05-15 08:27:11 +08:00
< p > 这里创建了两个线程,并将第二个线程所用的变量改名为 < code > handle2< / code > 和 < code > num2< / code > 。我们简化了例子,看是否能理解错误信息。此次编译给出如下信息:< / p >
2017-03-21 23:49:21 +08:00
< pre > < code class = "language-text" > error[E0382]: capture of moved value: `counter`
-->
|
8 | let handle = thread::spawn(move || {
| ------- value moved (into closure) here
...
2017-05-15 08:27:11 +08:00
16 | let mut num = counter.lock().unwrap();
| ^^^^^^^ value captured here after move
2017-03-21 23:49:21 +08:00
|
= note: move occurs because `counter` has type `std::sync::Mutex< i32> `,
which does not implement the `Copy` trait
error[E0382]: use of moved value: `counter`
-->
|
8 | let handle = thread::spawn(move || {
| ------- value moved (into closure) here
...
26 | println!(" Result: {}" , *counter.lock().unwrap());
| ^^^^^^^ value used here after move
|
= note: move occurs because `counter` has type `std::sync::Mutex< i32> `,
which does not implement the `Copy` trait
error: aborting due to 2 previous errors
< / code > < / pre >
2017-05-15 08:27:11 +08:00
< p > 啊哈!第一个错误信息中说,< code > counter< / code > 被移动进了 < code > handle< / code > 所代表线程的闭包中。因此我们无法在第二个线程中对其调用 < code > lock< / code > ,并将结果储存在 < code > num2< / code > 中时捕获< code > counter< / code > !所以 Rust 告诉我们不能将 < code > counter< / code > 的所有权移动到多个线程中。这在之前很难看出,因为我们在循环中创建了多个线程,而 Rust 无法在每次迭代中指明不同的线程(没有临时变量 < code > num2< / code > )。< / p >
2017-03-21 23:49:21 +08:00
< a class = "header" href = "#多线程和多所有权" name = "多线程和多所有权" > < h4 > 多线程和多所有权< / h4 > < / a >
2017-05-15 08:27:11 +08:00
< p > 在第十五章中,我们通过使用智能指针 < code > Rc< T> < / code > 来创建引用计数的值,以便拥有多所有权。同时第十五章提到了 < code > Rc< T> < / code > 只能在单线程环境中使用,不过还是在这里试用 < code > Rc< T> < / code > 看看会发生什么。列表 16-14 将 < code > Mutex< T> < / code > 装进了 < code > Rc< T> < / code > 中,并在移入线程之前克隆了 < code > Rc< T> < / code > 。再用循环来创建线程,保留闭包中的 < code > move< / code > 关键字:< / p >
2017-03-21 23:49:21 +08:00
< p > < span class = "filename" > Filename: src/main.rs< / span > < / p >
2017-05-15 08:27:11 +08:00
< pre > < code class = "language-rust" > use std::rc::Rc;
2017-03-21 23:49:21 +08:00
use std::sync::Mutex;
use std::thread;
fn main() {
let counter = Rc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = counter.clone();
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!(" Result: {}" , *counter.lock().unwrap());
}
< / code > < / pre >
< p > < span class = "caption" > Listing 16-14: Attempting to use < code > Rc< T> < / code > to allow
multiple threads to own the < code > Mutex< T> < / code > < / span > < / p >
2017-05-15 08:27:11 +08:00
< p > 再一次编译并...出现了不同的错误!编译器真是教会了我们很多!< / p >
2017-03-21 23:49:21 +08:00
< pre > < code > error[E0277]: the trait bound `std::rc::Rc< std::sync::Mutex< i32> > :
std::marker::Send` is not satisfied
-->
|
11 | let handle = thread::spawn(move || {
| ^^^^^^^^^^^^^ the trait `std::marker::Send` is not
implemented for `std::rc::Rc< std::sync::Mutex< i32> > `
|
= note: `std::rc::Rc< std::sync::Mutex< i32> > ` cannot be sent between threads
safely
= note: required because it appears within the type
`[closure@src/main.rs:11:36: 15:10
counter:std::rc::Rc< std::sync::Mutex< i32> > ]`
= note: required by `std::thread::spawn`
< / code > < / pre >
2017-05-15 08:27:11 +08:00
< p > 哇哦,太长不看!说重点:第一个提示表明 < code > Rc< Mutex< i32> > < / code > 不能安全的在线程间传递。理由也在错误信息中,“不满足 < code > Send< / code > trait bound”( < code > the trait bound Send is not satisfied< / code > )。下一部分将会讨论 < code > Send< / code > ,它是确保许多用在多线程中的类型,能够适合并发环境的 trait 之一。< / p >
< p > 不幸的是,< code > Rc< T> < / code > 并不能安全的在线程间共享。当 < code > Rc< T> < / code > 管理引用计数时,它必须在每一个 < code > clone< / code > 调用时增加计数,并在每一个克隆被丢弃时减少计数。< code > Rc< T> < / code > 并没有使用任何并发原语,来确保改变计数的操作不会被其他线程打断。在计数出错时可能会导致诡异的 bug, 比如可能会造成内存泄漏, 或在使用结束之前就丢弃一个值。如果有一个类型与 < code > Rc< T> < / code > 相似,又以一种线程安全的方式改变引用计数,会怎么样呢?< / p >
< a class = "header" href = "#原子引用计数-arct" name = "原子引用计数-arct" > < h4 > 原子引用计数 < code > Arc< T> < / code > < / h4 > < / a >
< p > 答案是肯定的,确实有一个类似< code > Rc< T> < / code > 并可以安全的用于并发环境的类型:< code > Arc< T> < / code > 。字母“a”代表< strong > 原子性< / strong > ( < em > atomic< / em > ),所以这是一个< strong > 原子引用计数< / strong > ( < em > atomically reference counted< / em > )类型。原子性是另一类这里还未涉及到的并发原语;请查看标准库中< code > std::sync::atomic< / code > 的文档来获取更多细节。其中的要点就是:原子性类型工作起来类似原始类型,不过可以安全的在线程间共享。< / p >
< p > 为什么不是所有的原始类型都是原子性的?为什么不是所有标准库中的类型都默认使用< code > Arc< T> < / code > 实现?线程安全带来性能惩罚,我们希望只在必要时才为此买单。如果只是在单线程中对值进行操作,原子性提供的保证并无必要,代码可以因此运行的更快。< / p >
< p > 回到之前的例子:< code > Arc< T> < / code > 和< code > Rc< T> < / code > 除了< code > Arc< T> < / code > 内部的原子性之外没有区别。其 API 也相同,所以可以修改< code > use< / code > 行和< code > new< / code > 调用。列表 16-15 中的代码最终可以编译和运行:< / p >
2017-03-21 23:49:21 +08:00
< p > < span class = "filename" > Filename: src/main.rs< / span > < / p >
< pre > < code class = "language-rust" > use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = counter.clone();
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!(" Result: {}" , *counter.lock().unwrap());
}
< / code > < / pre >
< p > < span class = "caption" > Listing 16-15: Using an < code > Arc< T> < / code > to wrap the < code > Mutex< T> < / code >
to be able to share ownership across multiple threads< / span > < / p >
< p > 这会打印出:< / p >
< pre > < code > Result: 10
< / code > < / pre >
2017-05-15 08:27:11 +08:00
< p > 成功了!我们从 0 数到了 10, 这可能并不是很显眼, 不过一路上我们学习了很多关于< code > Mutex< T> < / code > 和线程安全的内容!这个例子中构建的结构可以用于比增加计数更为复杂的操作。能够被分解为独立部分的计算可以像这样被分散到多个线程中,并可以使用< code > Mutex< T> < / code > 来允许每个线程在他们自己的部分更新最终的结果。< / p >
2017-03-21 23:49:21 +08:00
< p > 你可能注意到了,因为< code > counter< / code > 是不可变的,不过可以获取其内部值的可变引用,这意味着< code > Mutex< T> < / code > 提供了内部可变性,就像< code > Cell< / code > 系列类型那样。正如第十五章中使用< code > RefCell< T> < / code > 可以改变< code > Rc< T> < / code > 中的内容那样,同样的可以使用< code > Mutex< T> < / code > 来改变< code > Arc< T> < / code > 中的内容。< / p >
2017-05-15 08:27:11 +08:00
< p > 回忆一下< code > Rc< T> < / code > 并没有避免所有可能的问题:我们也讨论了当两个< code > Rc< T> < / code > 相互引用时的引用循环的可能性,这可能造成内存泄露。< code > Mutex< T> < / code > 有一个类似的 Rust 同样也不能避免的问题:死锁。< strong > 死锁< / strong > ( < em > deadlock< / em > )是一个场景中操作需要锁定两个资源,而两个线程分别拥有一个锁并永远相互等待的问题。如果你对这个主题感兴趣,尝试编写一个带有死锁的 Rust 程序,接着研究任何其他语言中使用互斥器的死锁规避策略并尝试在 Rust 中实现他们。标准库中< code > Mutex< T> < / code > 和< code > MutexGuard< / code > 的 API 文档会提供有用的信息。< / p >
< p > Rust 的类型系统和所有权规则,确保了线程在更新共享值时拥有独占的访问权限,所以线程不会以不可预测的方式覆盖彼此的操作。虽然为了使一切正确运行而在编译器上花了一些时间,但是我们节省了未来的时间,尤其是线程以特定顺序执行才会出现的诡异错误难以重现。< / p >
2017-03-22 15:30:00 +08:00
< p > 接下来,为了丰富本章的内容,让我们讨论一下< code > Send< / code > 和< code > Sync< / code > trait 以及如何对自定义类型使用他们。< / p >
2017-03-21 23:49:21 +08:00
2017-03-18 21:39:27 +08:00
< / div >
<!-- Mobile navigation buttons -->
< a href = "ch16-02-message-passing.html" class = "mobile-nav-chapters previous" >
< i class = "fa fa-angle-left" > < / i >
< / a >
< a href = "ch16-04-extensible-concurrency-sync-and-send.html" class = "mobile-nav-chapters next" >
< i class = "fa fa-angle-right" > < / i >
< / a >
< / div >
< a href = "ch16-02-message-passing.html" class = "nav-chapters previous" title = "You can navigate through the chapters using the arrow keys" >
< i class = "fa fa-angle-left" > < / i >
< / a >
< a href = "ch16-04-extensible-concurrency-sync-and-send.html" class = "nav-chapters next" title = "You can navigate through the chapters using the arrow keys" >
< i class = "fa fa-angle-right" > < / i >
< / a >
< / div >
<!-- Local fallback for Font Awesome -->
< script >
if ($(".fa").css("font-family") !== "FontAwesome") {
$('< link rel = "stylesheet" type = "text/css" href = "_FontAwesome/css/font-awesome.css" > ').prependTo('head');
}
< / script >
<!-- Livereload script (if served using the cli tool) -->
< script src = "highlight.js" > < / script >
< script src = "book.js" > < / script >
< / body >
< / html >