From 828953f4b3da73091ce45ea43671f7d52c6d12f8 Mon Sep 17 00:00:00 2001 From: KaiserY Date: Sat, 18 Mar 2017 21:39:27 +0800 Subject: [PATCH] wip start ch16 --- docs/ch01-00-introduction.html | 2 +- docs/ch01-01-installation.html | 2 +- docs/ch01-02-hello-world.html | 2 +- docs/ch02-00-guessing-game-tutorial.html | 2 +- docs/ch03-00-common-programming-concepts.html | 2 +- docs/ch03-01-variables-and-mutability.html | 2 +- docs/ch03-02-data-types.html | 2 +- docs/ch03-03-how-functions-work.html | 2 +- docs/ch03-04-comments.html | 2 +- docs/ch03-05-control-flow.html | 2 +- docs/ch04-00-understanding-ownership.html | 2 +- docs/ch04-01-what-is-ownership.html | 2 +- docs/ch04-02-references-and-borrowing.html | 2 +- docs/ch04-03-slices.html | 2 +- docs/ch05-00-structs.html | 2 +- docs/ch05-01-method-syntax.html | 2 +- docs/ch06-00-enums.html | 2 +- docs/ch06-01-defining-an-enum.html | 2 +- docs/ch06-02-match.html | 2 +- docs/ch06-03-if-let.html | 2 +- docs/ch07-00-modules.html | 2 +- docs/ch07-01-mod-and-the-filesystem.html | 2 +- ...07-02-controlling-visibility-with-pub.html | 2 +- docs/ch07-03-importing-names-with-use.html | 2 +- docs/ch08-00-common-collections.html | 2 +- docs/ch08-01-vectors.html | 2 +- docs/ch08-02-strings.html | 2 +- docs/ch08-03-hash-maps.html | 2 +- docs/ch09-00-error-handling.html | 2 +- ...09-01-unrecoverable-errors-with-panic.html | 2 +- ...h09-02-recoverable-errors-with-result.html | 2 +- docs/ch09-03-to-panic-or-not-to-panic.html | 2 +- docs/ch10-00-generics.html | 2 +- docs/ch10-01-syntax.html | 2 +- docs/ch10-02-traits.html | 2 +- docs/ch10-03-lifetime-syntax.html | 2 +- docs/ch11-00-testing.html | 2 +- docs/ch11-01-writing-tests.html | 2 +- docs/ch11-02-running-tests.html | 2 +- docs/ch11-03-test-organization.html | 2 +- docs/ch12-00-an-io-project.html | 2 +- ...2-01-accepting-command-line-arguments.html | 2 +- docs/ch12-02-reading-a-file.html | 2 +- ...proving-error-handling-and-modularity.html | 2 +- ...04-testing-the-librarys-functionality.html | 2 +- ...05-working-with-environment-variables.html | 2 +- ...6-writing-to-stderr-instead-of-stdout.html | 2 +- docs/ch13-00-functional-features.html | 2 +- docs/ch13-01-closures.html | 2 +- docs/ch13-02-iterators.html | 2 +- docs/ch13-03-improving-our-io-project.html | 2 +- docs/ch13-04-performance.html | 2 +- docs/ch14-00-more-about-cargo.html | 2 +- docs/ch14-01-release-profiles.html | 2 +- docs/ch14-02-publishing-to-crates-io.html | 2 +- docs/ch14-03-cargo-workspaces.html | 2 +- docs/ch14-04-installing-binaries.html | 2 +- docs/ch14-05-extending-cargo.html | 2 +- docs/ch15-00-smart-pointers.html | 2 +- docs/ch15-01-box.html | 2 +- docs/ch15-02-deref.html | 2 +- docs/ch15-03-drop.html | 2 +- docs/ch15-04-rc.html | 2 +- docs/ch15-05-interior-mutability.html | 2 +- docs/ch15-06-reference-cycles.html | 146 +++++++++++++- docs/ch16-00-concurrency.html | 136 +++++++++++++ docs/ch16-01-threads.html | 125 ++++++++++++ docs/ch16-02-message-passing.html | 116 +++++++++++ docs/ch16-03-shared-state.html | 116 +++++++++++ ...-extensible-concurrency-sync-and-send.html | 108 ++++++++++ docs/index.html | 2 +- docs/print.html | 169 +++++++++++++++- src/SUMMARY.md | 8 +- src/ch15-06-reference-cycles.md | 185 ++++++++++++++++++ src/ch16-00-concurrency.md | 21 ++ src/ch16-01-threads.md | 11 ++ src/ch16-02-message-passing.md | 0 src/ch16-03-shared-state.md | 0 ...04-extensible-concurrency-sync-and-send.md | 0 79 files changed, 1202 insertions(+), 69 deletions(-) create mode 100644 docs/ch16-00-concurrency.html create mode 100644 docs/ch16-01-threads.html create mode 100644 docs/ch16-02-message-passing.html create mode 100644 docs/ch16-03-shared-state.html create mode 100644 docs/ch16-04-extensible-concurrency-sync-and-send.html create mode 100644 src/ch16-00-concurrency.md create mode 100644 src/ch16-01-threads.md create mode 100644 src/ch16-02-message-passing.md create mode 100644 src/ch16-03-shared-state.md create mode 100644 src/ch16-04-extensible-concurrency-sync-and-send.md diff --git a/docs/ch01-00-introduction.html b/docs/ch01-00-introduction.html index 1d7caaf..52a6d89 100644 --- a/docs/ch01-00-introduction.html +++ b/docs/ch01-00-introduction.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-01-installation.html b/docs/ch01-01-installation.html index cbfaf05..4c0b0f2 100644 --- a/docs/ch01-01-installation.html +++ b/docs/ch01-01-installation.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-02-hello-world.html b/docs/ch01-02-hello-world.html index 314cb19..5880888 100644 --- a/docs/ch01-02-hello-world.html +++ b/docs/ch01-02-hello-world.html @@ -47,7 +47,7 @@
diff --git a/docs/ch02-00-guessing-game-tutorial.html b/docs/ch02-00-guessing-game-tutorial.html index bf621fa..2386177 100644 --- a/docs/ch02-00-guessing-game-tutorial.html +++ b/docs/ch02-00-guessing-game-tutorial.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-00-common-programming-concepts.html b/docs/ch03-00-common-programming-concepts.html index 96019ab..00b5f85 100644 --- a/docs/ch03-00-common-programming-concepts.html +++ b/docs/ch03-00-common-programming-concepts.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-01-variables-and-mutability.html b/docs/ch03-01-variables-and-mutability.html index c5095b0..66edbf2 100644 --- a/docs/ch03-01-variables-and-mutability.html +++ b/docs/ch03-01-variables-and-mutability.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-02-data-types.html b/docs/ch03-02-data-types.html index abaa629..a433032 100644 --- a/docs/ch03-02-data-types.html +++ b/docs/ch03-02-data-types.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-03-how-functions-work.html b/docs/ch03-03-how-functions-work.html index 9c7f536..fe4d28e 100644 --- a/docs/ch03-03-how-functions-work.html +++ b/docs/ch03-03-how-functions-work.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-04-comments.html b/docs/ch03-04-comments.html index b460acb..5b5c7ac 100644 --- a/docs/ch03-04-comments.html +++ b/docs/ch03-04-comments.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-05-control-flow.html b/docs/ch03-05-control-flow.html index c856ae7..92af582 100644 --- a/docs/ch03-05-control-flow.html +++ b/docs/ch03-05-control-flow.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-00-understanding-ownership.html b/docs/ch04-00-understanding-ownership.html index 1881367..19e73b3 100644 --- a/docs/ch04-00-understanding-ownership.html +++ b/docs/ch04-00-understanding-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-01-what-is-ownership.html b/docs/ch04-01-what-is-ownership.html index 1106852..c866973 100644 --- a/docs/ch04-01-what-is-ownership.html +++ b/docs/ch04-01-what-is-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-02-references-and-borrowing.html b/docs/ch04-02-references-and-borrowing.html index c3a1e7e..ac0a3f4 100644 --- a/docs/ch04-02-references-and-borrowing.html +++ b/docs/ch04-02-references-and-borrowing.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-03-slices.html b/docs/ch04-03-slices.html index 2b78660..968fe2b 100644 --- a/docs/ch04-03-slices.html +++ b/docs/ch04-03-slices.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-00-structs.html b/docs/ch05-00-structs.html index 3ae1224..e451f79 100644 --- a/docs/ch05-00-structs.html +++ b/docs/ch05-00-structs.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-01-method-syntax.html b/docs/ch05-01-method-syntax.html index 414a3d2..b85e850 100644 --- a/docs/ch05-01-method-syntax.html +++ b/docs/ch05-01-method-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-00-enums.html b/docs/ch06-00-enums.html index 1d2ea85..73e3dfc 100644 --- a/docs/ch06-00-enums.html +++ b/docs/ch06-00-enums.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-01-defining-an-enum.html b/docs/ch06-01-defining-an-enum.html index 98fede1..8aff8d7 100644 --- a/docs/ch06-01-defining-an-enum.html +++ b/docs/ch06-01-defining-an-enum.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-02-match.html b/docs/ch06-02-match.html index ffada68..f1502a2 100644 --- a/docs/ch06-02-match.html +++ b/docs/ch06-02-match.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-03-if-let.html b/docs/ch06-03-if-let.html index 930d73b..e0b13e6 100644 --- a/docs/ch06-03-if-let.html +++ b/docs/ch06-03-if-let.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-00-modules.html b/docs/ch07-00-modules.html index a628758..00cac28 100644 --- a/docs/ch07-00-modules.html +++ b/docs/ch07-00-modules.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-01-mod-and-the-filesystem.html b/docs/ch07-01-mod-and-the-filesystem.html index 33bf5d3..9a574bb 100644 --- a/docs/ch07-01-mod-and-the-filesystem.html +++ b/docs/ch07-01-mod-and-the-filesystem.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-02-controlling-visibility-with-pub.html b/docs/ch07-02-controlling-visibility-with-pub.html index 2850566..818f13b 100644 --- a/docs/ch07-02-controlling-visibility-with-pub.html +++ b/docs/ch07-02-controlling-visibility-with-pub.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-03-importing-names-with-use.html b/docs/ch07-03-importing-names-with-use.html index 0d0ba09..29ffa0a 100644 --- a/docs/ch07-03-importing-names-with-use.html +++ b/docs/ch07-03-importing-names-with-use.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-00-common-collections.html b/docs/ch08-00-common-collections.html index b3e1859..81c59d3 100644 --- a/docs/ch08-00-common-collections.html +++ b/docs/ch08-00-common-collections.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-01-vectors.html b/docs/ch08-01-vectors.html index b301ee3..45aae71 100644 --- a/docs/ch08-01-vectors.html +++ b/docs/ch08-01-vectors.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-02-strings.html b/docs/ch08-02-strings.html index 2642d16..3717eaf 100644 --- a/docs/ch08-02-strings.html +++ b/docs/ch08-02-strings.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-03-hash-maps.html b/docs/ch08-03-hash-maps.html index 1944409..1605034 100644 --- a/docs/ch08-03-hash-maps.html +++ b/docs/ch08-03-hash-maps.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-00-error-handling.html b/docs/ch09-00-error-handling.html index 76d97c2..2b4852d 100644 --- a/docs/ch09-00-error-handling.html +++ b/docs/ch09-00-error-handling.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-01-unrecoverable-errors-with-panic.html b/docs/ch09-01-unrecoverable-errors-with-panic.html index 4de9663..0c3715b 100644 --- a/docs/ch09-01-unrecoverable-errors-with-panic.html +++ b/docs/ch09-01-unrecoverable-errors-with-panic.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-02-recoverable-errors-with-result.html b/docs/ch09-02-recoverable-errors-with-result.html index c7871d1..fe609a4 100644 --- a/docs/ch09-02-recoverable-errors-with-result.html +++ b/docs/ch09-02-recoverable-errors-with-result.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-03-to-panic-or-not-to-panic.html b/docs/ch09-03-to-panic-or-not-to-panic.html index f1841a2..13d3978 100644 --- a/docs/ch09-03-to-panic-or-not-to-panic.html +++ b/docs/ch09-03-to-panic-or-not-to-panic.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-00-generics.html b/docs/ch10-00-generics.html index 48c9bdc..40cf871 100644 --- a/docs/ch10-00-generics.html +++ b/docs/ch10-00-generics.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-01-syntax.html b/docs/ch10-01-syntax.html index 26d079b..f24de51 100644 --- a/docs/ch10-01-syntax.html +++ b/docs/ch10-01-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-02-traits.html b/docs/ch10-02-traits.html index 9b40182..f63db0e 100644 --- a/docs/ch10-02-traits.html +++ b/docs/ch10-02-traits.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-03-lifetime-syntax.html b/docs/ch10-03-lifetime-syntax.html index 21e4879..5c49a5b 100644 --- a/docs/ch10-03-lifetime-syntax.html +++ b/docs/ch10-03-lifetime-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-00-testing.html b/docs/ch11-00-testing.html index a904a69..b86dc5b 100644 --- a/docs/ch11-00-testing.html +++ b/docs/ch11-00-testing.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-01-writing-tests.html b/docs/ch11-01-writing-tests.html index e82b4dd..3e844ad 100644 --- a/docs/ch11-01-writing-tests.html +++ b/docs/ch11-01-writing-tests.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-02-running-tests.html b/docs/ch11-02-running-tests.html index 80540a3..7ec57f9 100644 --- a/docs/ch11-02-running-tests.html +++ b/docs/ch11-02-running-tests.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-03-test-organization.html b/docs/ch11-03-test-organization.html index 2f5b0cc..572be63 100644 --- a/docs/ch11-03-test-organization.html +++ b/docs/ch11-03-test-organization.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-00-an-io-project.html b/docs/ch12-00-an-io-project.html index 8db3e6d..890c845 100644 --- a/docs/ch12-00-an-io-project.html +++ b/docs/ch12-00-an-io-project.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-01-accepting-command-line-arguments.html b/docs/ch12-01-accepting-command-line-arguments.html index 148533a..76c02c7 100644 --- a/docs/ch12-01-accepting-command-line-arguments.html +++ b/docs/ch12-01-accepting-command-line-arguments.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-02-reading-a-file.html b/docs/ch12-02-reading-a-file.html index 96ae2da..04aef1c 100644 --- a/docs/ch12-02-reading-a-file.html +++ b/docs/ch12-02-reading-a-file.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-03-improving-error-handling-and-modularity.html b/docs/ch12-03-improving-error-handling-and-modularity.html index aea8a60..dc8ff0a 100644 --- a/docs/ch12-03-improving-error-handling-and-modularity.html +++ b/docs/ch12-03-improving-error-handling-and-modularity.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-04-testing-the-librarys-functionality.html b/docs/ch12-04-testing-the-librarys-functionality.html index 6817002..d9b6c46 100644 --- a/docs/ch12-04-testing-the-librarys-functionality.html +++ b/docs/ch12-04-testing-the-librarys-functionality.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-05-working-with-environment-variables.html b/docs/ch12-05-working-with-environment-variables.html index 9c07b53..26e1d03 100644 --- a/docs/ch12-05-working-with-environment-variables.html +++ b/docs/ch12-05-working-with-environment-variables.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-06-writing-to-stderr-instead-of-stdout.html b/docs/ch12-06-writing-to-stderr-instead-of-stdout.html index 90915b5..570fbd6 100644 --- a/docs/ch12-06-writing-to-stderr-instead-of-stdout.html +++ b/docs/ch12-06-writing-to-stderr-instead-of-stdout.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-00-functional-features.html b/docs/ch13-00-functional-features.html index 7bab7f5..6796374 100644 --- a/docs/ch13-00-functional-features.html +++ b/docs/ch13-00-functional-features.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-01-closures.html b/docs/ch13-01-closures.html index 2432f69..258f109 100644 --- a/docs/ch13-01-closures.html +++ b/docs/ch13-01-closures.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-02-iterators.html b/docs/ch13-02-iterators.html index d673660..e6e18ae 100644 --- a/docs/ch13-02-iterators.html +++ b/docs/ch13-02-iterators.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-03-improving-our-io-project.html b/docs/ch13-03-improving-our-io-project.html index 67e9d0e..87ce6eb 100644 --- a/docs/ch13-03-improving-our-io-project.html +++ b/docs/ch13-03-improving-our-io-project.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-04-performance.html b/docs/ch13-04-performance.html index 0eab20c..35d082e 100644 --- a/docs/ch13-04-performance.html +++ b/docs/ch13-04-performance.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-00-more-about-cargo.html b/docs/ch14-00-more-about-cargo.html index 32f65f8..ce255d2 100644 --- a/docs/ch14-00-more-about-cargo.html +++ b/docs/ch14-00-more-about-cargo.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-01-release-profiles.html b/docs/ch14-01-release-profiles.html index eb6aa6e..6849119 100644 --- a/docs/ch14-01-release-profiles.html +++ b/docs/ch14-01-release-profiles.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-02-publishing-to-crates-io.html b/docs/ch14-02-publishing-to-crates-io.html index ce42c57..9dc5540 100644 --- a/docs/ch14-02-publishing-to-crates-io.html +++ b/docs/ch14-02-publishing-to-crates-io.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-03-cargo-workspaces.html b/docs/ch14-03-cargo-workspaces.html index 376bed9..9a3c96f 100644 --- a/docs/ch14-03-cargo-workspaces.html +++ b/docs/ch14-03-cargo-workspaces.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-04-installing-binaries.html b/docs/ch14-04-installing-binaries.html index c6153f3..4b84881 100644 --- a/docs/ch14-04-installing-binaries.html +++ b/docs/ch14-04-installing-binaries.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-05-extending-cargo.html b/docs/ch14-05-extending-cargo.html index 7632800..28cbd78 100644 --- a/docs/ch14-05-extending-cargo.html +++ b/docs/ch14-05-extending-cargo.html @@ -47,7 +47,7 @@
diff --git a/docs/ch15-00-smart-pointers.html b/docs/ch15-00-smart-pointers.html index dd96989..0487ae8 100644 --- a/docs/ch15-00-smart-pointers.html +++ b/docs/ch15-00-smart-pointers.html @@ -47,7 +47,7 @@
diff --git a/docs/ch15-01-box.html b/docs/ch15-01-box.html index 527b791..dd5fb6b 100644 --- a/docs/ch15-01-box.html +++ b/docs/ch15-01-box.html @@ -47,7 +47,7 @@
diff --git a/docs/ch15-02-deref.html b/docs/ch15-02-deref.html index 388b435..a57a909 100644 --- a/docs/ch15-02-deref.html +++ b/docs/ch15-02-deref.html @@ -47,7 +47,7 @@
diff --git a/docs/ch15-03-drop.html b/docs/ch15-03-drop.html index 0a4f9f5..0d5e648 100644 --- a/docs/ch15-03-drop.html +++ b/docs/ch15-03-drop.html @@ -47,7 +47,7 @@
diff --git a/docs/ch15-04-rc.html b/docs/ch15-04-rc.html index 187f2ae..2b500c1 100644 --- a/docs/ch15-04-rc.html +++ b/docs/ch15-04-rc.html @@ -47,7 +47,7 @@
diff --git a/docs/ch15-05-interior-mutability.html b/docs/ch15-05-interior-mutability.html index a3bdf7a..e2e0edf 100644 --- a/docs/ch15-05-interior-mutability.html +++ b/docs/ch15-05-interior-mutability.html @@ -47,7 +47,7 @@
diff --git a/docs/ch15-06-reference-cycles.html b/docs/ch15-06-reference-cycles.html index 2ede239..8ebe28e 100644 --- a/docs/ch15-06-reference-cycles.html +++ b/docs/ch15-06-reference-cycles.html @@ -47,7 +47,7 @@
@@ -146,6 +146,142 @@ values pointing to each other

Reference cycle of lists

Figure 15-18: A reference cycle of lists a and b pointing to each other

+

如果你注释掉最后的println!,Rust 会尝试打印出a指向b指向a这样的循环直到栈溢出。

+

观察最后一个println!之前的打印结果,就会发现在将a改变为指向b之后ab的引用计数都是 2。在main的结尾,Rust 首先会尝试丢弃b,这会使Rc的引用计数减一,但是这个计数是 1 而不是 0,所以Rc在堆上的内存不会被丢弃。它只是会永远的停留在 1 上。这个特定例子中,程序立马就结束了,所以并不是一个问题,不过如果是一个更加复杂的程序,它在这个循环中分配了很多内存并占有很长时间,这就是个问题了。这个程序会使用多于它所需要的内存,并有可能压垮系统并造成没有内存可供使用。

+

现在,如你所见,在 Rust 中创建引用循环是困难和繁琐的。但并不是不可能:避免引用循环这种形式的内存泄漏并不是 Rust 的保证之一。如果你有包含Rc<T>RefCell<T>值或类似的嵌套结合了内部可变性和引用计数的类型,请务必小心确保你没有形成一个引用循环。在列表 15-14 的例子中,可能解决方式就是不要编写像这样可能造成引用循环的代码,因为我们希望Cons成员拥有他们指向的列表。

+

举例来说,对于像图这样的数据结构,为了创建父节点指向子节点的边和以相反方向从子节点指向父节点的边,有时需要创建这样的引用循环。如果一个方向拥有所有权而另一个方向没有,对于模拟这种数据关系的一种不会创建引用循环和内存泄露的方式是使用Weak<T>。接下来让我们探索一下!

+

避免引用循环:将Rc<T>变为Weak<T>

+

Rust 标准库中提供了Weak<T>,一个用于存在引用循环但只有一个方向有所有权的智能指针。我们已经展示过如何克隆Rc<T>来增加引用的strong_countWeak<T>是一种引用Rc<T>但不增加strong_count的方式:相反它增加Rc引用的weak_count。当Rc离开作用域,其内部值会在strong_count为 0 的时候被丢弃,即便weak_count不为 0 。为了能够从Weak<T>中获取值,首先需要使用upgrade方法将其升级为Option<Rc<T>>。升级Weak<T>的结果在Rc还未被丢弃时是Some,而在Rc被丢弃时是None。因为upgrade返回一个Option,我们知道 Rust 会确保SomeNone的情况都被处理并不会尝试使用一个无效的指针。

+

不同于列表 15-17 中每个项只知道它的下一项,加入我们需要一个树,它的项知道它的子项父项。

+

让我们从一个叫做Node的存放拥有所有权的i32值和其子Node值的引用的结构体开始:

+
use std::rc::Rc;
+use std::cell::RefCell;
+
+#[derive(Debug)]
+struct Node {
+    value: i32,
+    children: RefCell<Vec<Rc<Node>>>,
+}
+
+

我们希望能够Node拥有其子节点,同时也希望变量可以拥有每个节点以便可以直接访问他们。这就是为什么Vec中的项是Rc<Node>值。我们也希望能够修改其他节点的子节点,这就是为什么childrenVec被放进了RefCell的原因。在列表 15-19 中创建了一个叫做leaf的带有值 3 并没有子节点的Node实例,和另一个带有值 5 和以leaf作为子节点的实例branch

+

Filename: src/main.rs

+
fn main() {
+    let leaf = Rc::new(Node {
+        value: 3,
+        children: RefCell::new(vec![]),
+    });
+
+    let branch = Rc::new(Node {
+        value: 5,
+        children: RefCell::new(vec![leaf.clone()]),
+    });
+}
+
+

Listing 15-19: Creating a leaf node and a branch node +where branch has leaf as one of its children but leaf has no reference to +branch

+

leaf中的Node现在有两个所有者:leafbranch,因为我们克隆了leaf中的Rc并储存在了branch中。branch中的Node知道它与leaf相关联因为branchbranch.children中有leaf的引用。然而,leaf并不知道它与branch相关联,而我们希望leaf知道branch是其父节点。

+

为了做到这一点,需要在Node结构体定义中增加一个parent字段,不过parent的类型应该是什么呢?我们知道它不能包含Rc<T>,因为这样leaf.parent将会指向branchbranch.children会包含leaf的指针,这会形成引用循环。leafbranch不会被丢弃因为他们总是引用对方且引用计数永远也不会是零。

+

所以在parent的类型中是使用Weak<T>而不是Rc,具体来说是RefCell<Weak<Node>>

+

Filename: src/main.rs

+
use std::rc::{Rc, Weak};
+use std::cell::RefCell;
+
+#[derive(Debug)]
+struct Node {
+    value: i32,
+    parent: RefCell<Weak<Node>>,
+    children: RefCell<Vec<Rc<Node>>>,
+}
+
+

这样,一个节点就能够在拥有父节点时指向它,而并不拥有其父节点。一个父节点哪怕在拥有指向它的子节点也会被丢弃,只要是其自身也没有一个父节点就行。现在将main函数更新为如列表 15-20 所示:

+

Filename: src/main.rs

+
fn main() {
+    let leaf = Rc::new(Node {
+        value: 3,
+        parent: RefCell::new(Weak::new()),
+        children: RefCell::new(vec![]),
+    });
+
+    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+
+    let branch = Rc::new(Node {
+        value: 5,
+        parent: RefCell::new(Weak::new()),
+        children: RefCell::new(vec![leaf.clone()]),
+    });
+
+    *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
+
+    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+}
+
+

Listing 15-20: A leaf node and a branch node where +leaf has a Weak reference to its parent, branch

+

创建leaf节点是类似的;因为它作为开始并没有父节点,这里创建了一个新的Weak引用实例。当尝试通过upgrade方法获取leaf父节点的引用时,会得到一个None值,如第一个println!输出所示:

+
leaf parent = None
+
+

类似的,branch也有一个新的Weak引用,因为也没有父节点。leaf仍然作为branch的一个子节点。一旦在branch中有了一个新的Node实例,就可以修改leaf将一个branchWeak引用作为其父节点。这里使用了leafparent字段里的RefCellborrow_mut方法,接着使用了Rc::downgrade函数来从branch中的Rc值创建了一个指向branchWeak引用。

+

当再次打印出leaf的父节点时,这一次将会得到存放了branchSome值。另外需要注意到这里并没有打印出类似列表 15-14 中那样最终导致栈溢出的循环:Weak引用仅仅打印出(Weak)

+
leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) },
+children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) },
+children: RefCell { value: [] } }] } })
+
+

没有无限的输出(或直到栈溢出)的事实表明这里并没有引用循环。另一种证明的方式时观察调用Rc::strong_countRc::weak_count的值。在列表 15-21 中,创建了一个新的内部作用域并将branch的创建放入其中,这样可以观察branch被创建时和离开作用域被丢弃时发生了什么:

+

Filename: src/main.rs

+
fn main() {
+    let leaf = Rc::new(Node {
+        value: 3,
+        parent: RefCell::new(Weak::new()),
+        children: RefCell::new(vec![]),
+    });
+
+    println!(
+        "leaf strong = {}, weak = {}",
+        Rc::strong_count(&leaf),
+        Rc::weak_count(&leaf),
+    );
+
+    {
+        let branch = Rc::new(Node {
+            value: 5,
+            parent: RefCell::new(Weak::new()),
+            children: RefCell::new(vec![leaf.clone()]),
+        });
+        *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
+
+        println!(
+            "branch strong = {}, weak = {}",
+            Rc::strong_count(&branch),
+            Rc::weak_count(&branch),
+        );
+
+        println!(
+            "leaf strong = {}, weak = {}",
+            Rc::strong_count(&leaf),
+            Rc::weak_count(&leaf),
+        );
+    }
+
+    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+    println!(
+        "leaf strong = {}, weak = {}",
+        Rc::strong_count(&leaf),
+        Rc::weak_count(&leaf),
+    );
+}
+
+

Listing 15-21: Creating branch in an inner scope and +examining strong and weak reference counts of leaf and branch

+

创建leaf之后,强引用计数是 1 (用于leaf自身)而弱引用计数是 0。在内部作用域中,在创建branch和关联leafbranch之后,branch的强引用计数为 1(用于branch自身)而弱引用计数为 1(因为leaf.parent通过一个Weak<T>指向branch)。leaf的强引用计数为 2,因为branch现在有一个leaf克隆的Rc储存在branch.children中。leaf的弱引用计数仍然为 0。

+

当内部作用域结束,branch离开作用域,其强引用计数减少为 0,所以其Node被丢弃。来自leaf.parent的弱引用计数 1 与Node是否被丢弃无关,所以并没有产生内存泄露!

+

如果在内部作用域结束后尝试访问leaf的父节点,会像leaf拥有父节点之前一样得到None值。在程序的末尾,leaf的强引用计数为 1 而弱引用计数为 0,因为现在leaf又是唯一指向其自己的值了。

+

所有这些管理计数和值是否应该被丢弃的逻辑都通过RcWeak和他们的Drop trait 实现来控制。通过在定义中指定从子节点到父节点的关系为一个Weak<T>引用,就能够拥有父节点和子节点之间的双向引用而不会造成引用循环和内存泄露。

+

总结

+

现在我们学习了如何选择不同类型的智能指针来选择不同的保证并与 Rust 的常规引用向取舍。Box<T>有一个已知的大小并指向分配在堆上的数据。Rc<T>记录了堆上数据的引用数量这样就可以拥有多个所有者。RefCell<T>和其内部可变性使其可以用于需要不可变类型,但希望在运行时而不是编译时检查借用规则的场景。

+

我们还介绍了提供了很多智能指针功能的 trait DerefDrop。同时探索了形成引用循环和造成内存泄漏的可能性,以及如何使用Weak<T>避免引用循环。

+

如果本章内容引起了你的兴趣并希望现在就实现你自己的智能指针的话,请阅读 The Nomicon 来获取更多有用的信息。

+

接下来,让我们谈谈 Rust 的并发。我们还会学习到一些新的堆并发有帮助的智能指针。

@@ -157,6 +293,10 @@ pointing to each other

+ +
@@ -167,6 +307,10 @@ pointing to each other

+ +
diff --git a/docs/ch16-00-concurrency.html b/docs/ch16-00-concurrency.html new file mode 100644 index 0000000..10c247b --- /dev/null +++ b/docs/ch16-00-concurrency.html @@ -0,0 +1,136 @@ + + + + + 无畏并发 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+

无畏并发

+
+

ch16-00-concurrency.md +
+commit da15de39eaabd50100d6fa662c653169254d9175

+
+

确保内存安全并不是 Rust 的唯一目标:作为一个能更好的处理并发和并行编程一直是 Rust 的另一个主要目标。 +并发编程(concurrent programming)代表程序的不同部分相互独立的执行,而并行编程代表程序不同部分同时执行,这两个概念在计算机拥有更多处理器可供程序利用时变得更加重要。由于历史的原因,在此类上下文中编程一直是困难且容易出错的:Rust 希望能改变这一点。

+

最开始,我们认为内存安全和防止并发问题是需要通过两个不同的方法解决的两个相互独立的挑战。然而,随着时间的推移,我们发现所有权和类型系统是一系列解决内存安全并发问题的强用力的工具!通过改进所有权和类型检查,很多并发错误在 Rust 中都是编译时错误,而不是运行时错误。我们给 Rust 的这一部分起了一个绰号无畏并发fearless concurrency)。无畏并发意味着 Rust 不光允许你自信代码不会出现诡异的错误,也让你可以轻易重构这种代码而无需担心会引入新的 bug。

+
+

注意:对于 Rust 的口号无畏并发,这里用并发指代很多问题而不是更精确的区分并发和(或)并行,是处于简化问题的原因。如果这是一本专注于并发和/或并行的书,我们肯定会更精确的。对于本章,请自行脑补任何并发并发和(或)并行

+
+

很多语言对于其所提供的处理并发并发问题的解决方法是非常固执己见的。这是一个非常合理的策略,尤其是对于更高级的语言来说,不过对于底层语言来说可没有奢侈的选择。底层语言被期望为能在任何给定的场景中启用提供最高性能的方法,同时他们对硬件有更少的抽象。因此,Rust 给了我们多种工具来以适合场景和要求的方式来为问题建模。

+

如下是本章将要涉及到的内容:

+
    +
  • 如果创建线程来同时运行多段代码。
  • +
  • 并发消息传递Message passing),其中通道(channel)被用来在线程间传递消息。
  • +
  • 并发共享状态Shared state),其中多个线程可以访问同一片数据。
  • +
  • SyncSend trait,他们允许 Rust 的并发保证能扩展到用户定义的和标准库中提供的类型中。
  • +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch16-01-threads.html b/docs/ch16-01-threads.html new file mode 100644 index 0000000..3ea00c4 --- /dev/null +++ b/docs/ch16-01-threads.html @@ -0,0 +1,125 @@ + + + + + 线程 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+

使用线程同时运行代码

+
+

ch16-01-threads.md +
+commit 55b294f20fc846a13a9be623bf322d8b364cee77

+
+

在今天使用的大部分操作系统中,当程序执行时,操作系统运行代码的上下文称为进程process)。操作系统可以运行很多进程,而操作系统也管理这些进程使得多个程序可以在电脑上同时运行。

+

我们可以将每个进程运行一个程序的概念再往下抽象一层:程序也可以在其上下文中同时运行独立的部分。这个功能叫做线程thread)。

+

将程序需要执行的计算拆分到多个线程中可以提高性能,因为程序可以在同时进行很多工作。

+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch16-02-message-passing.html b/docs/ch16-02-message-passing.html new file mode 100644 index 0000000..9abb018 --- /dev/null +++ b/docs/ch16-02-message-passing.html @@ -0,0 +1,116 @@ + + + + + 消息传递 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch16-03-shared-state.html b/docs/ch16-03-shared-state.html new file mode 100644 index 0000000..95fa581 --- /dev/null +++ b/docs/ch16-03-shared-state.html @@ -0,0 +1,116 @@ + + + + + 共享状态 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch16-04-extensible-concurrency-sync-and-send.html b/docs/ch16-04-extensible-concurrency-sync-and-send.html new file mode 100644 index 0000000..27877a8 --- /dev/null +++ b/docs/ch16-04-extensible-concurrency-sync-and-send.html @@ -0,0 +1,108 @@ + + + + + 可扩展的并发:`Sync`和`Send` - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/index.html b/docs/index.html index e6df29c..3e311bd 100644 --- a/docs/index.html +++ b/docs/index.html @@ -46,7 +46,7 @@
diff --git a/docs/print.html b/docs/print.html index 8398e6d..5a43c7c 100644 --- a/docs/print.html +++ b/docs/print.html @@ -2,7 +2,7 @@ - 引用循环和内存泄漏是安全的 - Rust 程序设计语言 简体中文版 + 可扩展的并发:`Sync`和`Send` - Rust 程序设计语言 简体中文版 @@ -47,7 +47,7 @@
@@ -8728,6 +8728,171 @@ values pointing to each other

Reference cycle of lists

Figure 15-18: A reference cycle of lists a and b pointing to each other

+

如果你注释掉最后的println!,Rust 会尝试打印出a指向b指向a这样的循环直到栈溢出。

+

观察最后一个println!之前的打印结果,就会发现在将a改变为指向b之后ab的引用计数都是 2。在main的结尾,Rust 首先会尝试丢弃b,这会使Rc的引用计数减一,但是这个计数是 1 而不是 0,所以Rc在堆上的内存不会被丢弃。它只是会永远的停留在 1 上。这个特定例子中,程序立马就结束了,所以并不是一个问题,不过如果是一个更加复杂的程序,它在这个循环中分配了很多内存并占有很长时间,这就是个问题了。这个程序会使用多于它所需要的内存,并有可能压垮系统并造成没有内存可供使用。

+

现在,如你所见,在 Rust 中创建引用循环是困难和繁琐的。但并不是不可能:避免引用循环这种形式的内存泄漏并不是 Rust 的保证之一。如果你有包含Rc<T>RefCell<T>值或类似的嵌套结合了内部可变性和引用计数的类型,请务必小心确保你没有形成一个引用循环。在列表 15-14 的例子中,可能解决方式就是不要编写像这样可能造成引用循环的代码,因为我们希望Cons成员拥有他们指向的列表。

+

举例来说,对于像图这样的数据结构,为了创建父节点指向子节点的边和以相反方向从子节点指向父节点的边,有时需要创建这样的引用循环。如果一个方向拥有所有权而另一个方向没有,对于模拟这种数据关系的一种不会创建引用循环和内存泄露的方式是使用Weak<T>。接下来让我们探索一下!

+

避免引用循环:将Rc<T>变为Weak<T>

+

Rust 标准库中提供了Weak<T>,一个用于存在引用循环但只有一个方向有所有权的智能指针。我们已经展示过如何克隆Rc<T>来增加引用的strong_countWeak<T>是一种引用Rc<T>但不增加strong_count的方式:相反它增加Rc引用的weak_count。当Rc离开作用域,其内部值会在strong_count为 0 的时候被丢弃,即便weak_count不为 0 。为了能够从Weak<T>中获取值,首先需要使用upgrade方法将其升级为Option<Rc<T>>。升级Weak<T>的结果在Rc还未被丢弃时是Some,而在Rc被丢弃时是None。因为upgrade返回一个Option,我们知道 Rust 会确保SomeNone的情况都被处理并不会尝试使用一个无效的指针。

+

不同于列表 15-17 中每个项只知道它的下一项,加入我们需要一个树,它的项知道它的子项父项。

+

让我们从一个叫做Node的存放拥有所有权的i32值和其子Node值的引用的结构体开始:

+
use std::rc::Rc;
+use std::cell::RefCell;
+
+#[derive(Debug)]
+struct Node {
+    value: i32,
+    children: RefCell<Vec<Rc<Node>>>,
+}
+
+

我们希望能够Node拥有其子节点,同时也希望变量可以拥有每个节点以便可以直接访问他们。这就是为什么Vec中的项是Rc<Node>值。我们也希望能够修改其他节点的子节点,这就是为什么childrenVec被放进了RefCell的原因。在列表 15-19 中创建了一个叫做leaf的带有值 3 并没有子节点的Node实例,和另一个带有值 5 和以leaf作为子节点的实例branch

+

Filename: src/main.rs

+
fn main() {
+    let leaf = Rc::new(Node {
+        value: 3,
+        children: RefCell::new(vec![]),
+    });
+
+    let branch = Rc::new(Node {
+        value: 5,
+        children: RefCell::new(vec![leaf.clone()]),
+    });
+}
+
+

Listing 15-19: Creating a leaf node and a branch node +where branch has leaf as one of its children but leaf has no reference to +branch

+

leaf中的Node现在有两个所有者:leafbranch,因为我们克隆了leaf中的Rc并储存在了branch中。branch中的Node知道它与leaf相关联因为branchbranch.children中有leaf的引用。然而,leaf并不知道它与branch相关联,而我们希望leaf知道branch是其父节点。

+

为了做到这一点,需要在Node结构体定义中增加一个parent字段,不过parent的类型应该是什么呢?我们知道它不能包含Rc<T>,因为这样leaf.parent将会指向branchbranch.children会包含leaf的指针,这会形成引用循环。leafbranch不会被丢弃因为他们总是引用对方且引用计数永远也不会是零。

+

所以在parent的类型中是使用Weak<T>而不是Rc,具体来说是RefCell<Weak<Node>>

+

Filename: src/main.rs

+
use std::rc::{Rc, Weak};
+use std::cell::RefCell;
+
+#[derive(Debug)]
+struct Node {
+    value: i32,
+    parent: RefCell<Weak<Node>>,
+    children: RefCell<Vec<Rc<Node>>>,
+}
+
+

这样,一个节点就能够在拥有父节点时指向它,而并不拥有其父节点。一个父节点哪怕在拥有指向它的子节点也会被丢弃,只要是其自身也没有一个父节点就行。现在将main函数更新为如列表 15-20 所示:

+

Filename: src/main.rs

+
fn main() {
+    let leaf = Rc::new(Node {
+        value: 3,
+        parent: RefCell::new(Weak::new()),
+        children: RefCell::new(vec![]),
+    });
+
+    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+
+    let branch = Rc::new(Node {
+        value: 5,
+        parent: RefCell::new(Weak::new()),
+        children: RefCell::new(vec![leaf.clone()]),
+    });
+
+    *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
+
+    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+}
+
+

Listing 15-20: A leaf node and a branch node where +leaf has a Weak reference to its parent, branch

+

创建leaf节点是类似的;因为它作为开始并没有父节点,这里创建了一个新的Weak引用实例。当尝试通过upgrade方法获取leaf父节点的引用时,会得到一个None值,如第一个println!输出所示:

+
leaf parent = None
+
+

类似的,branch也有一个新的Weak引用,因为也没有父节点。leaf仍然作为branch的一个子节点。一旦在branch中有了一个新的Node实例,就可以修改leaf将一个branchWeak引用作为其父节点。这里使用了leafparent字段里的RefCellborrow_mut方法,接着使用了Rc::downgrade函数来从branch中的Rc值创建了一个指向branchWeak引用。

+

当再次打印出leaf的父节点时,这一次将会得到存放了branchSome值。另外需要注意到这里并没有打印出类似列表 15-14 中那样最终导致栈溢出的循环:Weak引用仅仅打印出(Weak)

+
leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) },
+children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) },
+children: RefCell { value: [] } }] } })
+
+

没有无限的输出(或直到栈溢出)的事实表明这里并没有引用循环。另一种证明的方式时观察调用Rc::strong_countRc::weak_count的值。在列表 15-21 中,创建了一个新的内部作用域并将branch的创建放入其中,这样可以观察branch被创建时和离开作用域被丢弃时发生了什么:

+

Filename: src/main.rs

+
fn main() {
+    let leaf = Rc::new(Node {
+        value: 3,
+        parent: RefCell::new(Weak::new()),
+        children: RefCell::new(vec![]),
+    });
+
+    println!(
+        "leaf strong = {}, weak = {}",
+        Rc::strong_count(&leaf),
+        Rc::weak_count(&leaf),
+    );
+
+    {
+        let branch = Rc::new(Node {
+            value: 5,
+            parent: RefCell::new(Weak::new()),
+            children: RefCell::new(vec![leaf.clone()]),
+        });
+        *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
+
+        println!(
+            "branch strong = {}, weak = {}",
+            Rc::strong_count(&branch),
+            Rc::weak_count(&branch),
+        );
+
+        println!(
+            "leaf strong = {}, weak = {}",
+            Rc::strong_count(&leaf),
+            Rc::weak_count(&leaf),
+        );
+    }
+
+    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+    println!(
+        "leaf strong = {}, weak = {}",
+        Rc::strong_count(&leaf),
+        Rc::weak_count(&leaf),
+    );
+}
+
+

Listing 15-21: Creating branch in an inner scope and +examining strong and weak reference counts of leaf and branch

+

创建leaf之后,强引用计数是 1 (用于leaf自身)而弱引用计数是 0。在内部作用域中,在创建branch和关联leafbranch之后,branch的强引用计数为 1(用于branch自身)而弱引用计数为 1(因为leaf.parent通过一个Weak<T>指向branch)。leaf的强引用计数为 2,因为branch现在有一个leaf克隆的Rc储存在branch.children中。leaf的弱引用计数仍然为 0。

+

当内部作用域结束,branch离开作用域,其强引用计数减少为 0,所以其Node被丢弃。来自leaf.parent的弱引用计数 1 与Node是否被丢弃无关,所以并没有产生内存泄露!

+

如果在内部作用域结束后尝试访问leaf的父节点,会像leaf拥有父节点之前一样得到None值。在程序的末尾,leaf的强引用计数为 1 而弱引用计数为 0,因为现在leaf又是唯一指向其自己的值了。

+

所有这些管理计数和值是否应该被丢弃的逻辑都通过RcWeak和他们的Drop trait 实现来控制。通过在定义中指定从子节点到父节点的关系为一个Weak<T>引用,就能够拥有父节点和子节点之间的双向引用而不会造成引用循环和内存泄露。

+

总结

+

现在我们学习了如何选择不同类型的智能指针来选择不同的保证并与 Rust 的常规引用向取舍。Box<T>有一个已知的大小并指向分配在堆上的数据。Rc<T>记录了堆上数据的引用数量这样就可以拥有多个所有者。RefCell<T>和其内部可变性使其可以用于需要不可变类型,但希望在运行时而不是编译时检查借用规则的场景。

+

我们还介绍了提供了很多智能指针功能的 trait DerefDrop。同时探索了形成引用循环和造成内存泄漏的可能性,以及如何使用Weak<T>避免引用循环。

+

如果本章内容引起了你的兴趣并希望现在就实现你自己的智能指针的话,请阅读 The Nomicon 来获取更多有用的信息。

+

接下来,让我们谈谈 Rust 的并发。我们还会学习到一些新的堆并发有帮助的智能指针。

+

无畏并发

+
+

ch16-00-concurrency.md +
+commit da15de39eaabd50100d6fa662c653169254d9175

+
+

确保内存安全并不是 Rust 的唯一目标:作为一个能更好的处理并发和并行编程一直是 Rust 的另一个主要目标。 +并发编程(concurrent programming)代表程序的不同部分相互独立的执行,而并行编程代表程序不同部分同时执行,这两个概念在计算机拥有更多处理器可供程序利用时变得更加重要。由于历史的原因,在此类上下文中编程一直是困难且容易出错的:Rust 希望能改变这一点。

+

最开始,我们认为内存安全和防止并发问题是需要通过两个不同的方法解决的两个相互独立的挑战。然而,随着时间的推移,我们发现所有权和类型系统是一系列解决内存安全并发问题的强用力的工具!通过改进所有权和类型检查,很多并发错误在 Rust 中都是编译时错误,而不是运行时错误。我们给 Rust 的这一部分起了一个绰号无畏并发fearless concurrency)。无畏并发意味着 Rust 不光允许你自信代码不会出现诡异的错误,也让你可以轻易重构这种代码而无需担心会引入新的 bug。

+
+

注意:对于 Rust 的口号无畏并发,这里用并发指代很多问题而不是更精确的区分并发和(或)并行,是处于简化问题的原因。如果这是一本专注于并发和/或并行的书,我们肯定会更精确的。对于本章,请自行脑补任何并发并发和(或)并行

+
+

很多语言对于其所提供的处理并发并发问题的解决方法是非常固执己见的。这是一个非常合理的策略,尤其是对于更高级的语言来说,不过对于底层语言来说可没有奢侈的选择。底层语言被期望为能在任何给定的场景中启用提供最高性能的方法,同时他们对硬件有更少的抽象。因此,Rust 给了我们多种工具来以适合场景和要求的方式来为问题建模。

+

如下是本章将要涉及到的内容:

+
    +
  • 如果创建线程来同时运行多段代码。
  • +
  • 并发消息传递Message passing),其中通道(channel)被用来在线程间传递消息。
  • +
  • 并发共享状态Shared state),其中多个线程可以访问同一片数据。
  • +
  • SyncSend trait,他们允许 Rust 的并发保证能扩展到用户定义的和标准库中提供的类型中。
  • +
+

使用线程同时运行代码

+
+

ch16-01-threads.md +
+commit 55b294f20fc846a13a9be623bf322d8b364cee77

+
+

在今天使用的大部分操作系统中,当程序执行时,操作系统运行代码的上下文称为进程process)。操作系统可以运行很多进程,而操作系统也管理这些进程使得多个程序可以在电脑上同时运行。

+

我们可以将每个进程运行一个程序的概念再往下抽象一层:程序也可以在其上下文中同时运行独立的部分。这个功能叫做线程thread)。

+

将程序需要执行的计算拆分到多个线程中可以提高性能,因为程序可以在同时进行很多工作。

diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 620a10e..841e239 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -84,4 +84,10 @@ - [`Drop` Trait 运行清理代码](ch15-03-drop.md) - [`Rc` 引用计数智能指针](ch15-04-rc.md) - [`RefCell`和内部可变性模式](ch15-05-interior-mutability.md) - - [引用循环和内存泄漏是安全的](ch15-06-reference-cycles.md) \ No newline at end of file + - [引用循环和内存泄漏是安全的](ch15-06-reference-cycles.md) + +- [无畏并发](ch16-00-concurrency.md) + - [线程](ch16-01-threads.md) + - [消息传递](ch16-02-message-passing.md) + - [共享状态](ch16-03-shared-state.md) + - [可扩展的并发:`Sync`和`Send`](ch16-04-extensible-concurrency-sync-and-send.md) \ No newline at end of file diff --git a/src/ch15-06-reference-cycles.md b/src/ch15-06-reference-cycles.md index 773a305..c3b57c4 100644 --- a/src/ch15-06-reference-cycles.md +++ b/src/ch15-06-reference-cycles.md @@ -92,3 +92,188 @@ values pointing to each other Figure 15-18: A reference cycle of lists `a` and `b` pointing to each other +如果你注释掉最后的`println!`,Rust 会尝试打印出`a`指向`b`指向`a`这样的循环直到栈溢出。 + +观察最后一个`println!`之前的打印结果,就会发现在将`a`改变为指向`b`之后`a`和`b`的引用计数都是 2。在`main`的结尾,Rust 首先会尝试丢弃`b`,这会使`Rc`的引用计数减一,但是这个计数是 1 而不是 0,所以`Rc`在堆上的内存不会被丢弃。它只是会永远的停留在 1 上。这个特定例子中,程序立马就结束了,所以并不是一个问题,不过如果是一个更加复杂的程序,它在这个循环中分配了很多内存并占有很长时间,这就是个问题了。这个程序会使用多于它所需要的内存,并有可能压垮系统并造成没有内存可供使用。 + +现在,如你所见,在 Rust 中创建引用循环是困难和繁琐的。但并不是不可能:避免引用循环这种形式的内存泄漏并不是 Rust 的保证之一。如果你有包含`Rc`的`RefCell`值或类似的嵌套结合了内部可变性和引用计数的类型,请务必小心确保你没有形成一个引用循环。在列表 15-14 的例子中,可能解决方式就是不要编写像这样可能造成引用循环的代码,因为我们希望`Cons`成员拥有他们指向的列表。 + +举例来说,对于像图这样的数据结构,为了创建父节点指向子节点的边和以相反方向从子节点指向父节点的边,有时需要创建这样的引用循环。如果一个方向拥有所有权而另一个方向没有,对于模拟这种数据关系的一种不会创建引用循环和内存泄露的方式是使用`Weak`。接下来让我们探索一下! + +### 避免引用循环:将`Rc`变为`Weak` + +Rust 标准库中提供了`Weak`,一个用于存在引用循环但只有一个方向有所有权的智能指针。我们已经展示过如何克隆`Rc`来增加引用的`strong_count`;`Weak`是一种引用`Rc`但不增加`strong_count`的方式:相反它增加`Rc`引用的`weak_count`。当`Rc`离开作用域,其内部值会在`strong_count`为 0 的时候被丢弃,即便`weak_count`不为 0 。为了能够从`Weak`中获取值,首先需要使用`upgrade`方法将其升级为`Option>`。升级`Weak`的结果在`Rc`还未被丢弃时是`Some`,而在`Rc`被丢弃时是`None`。因为`upgrade`返回一个`Option`,我们知道 Rust 会确保`Some`和`None`的情况都被处理并不会尝试使用一个无效的指针。 + +不同于列表 15-17 中每个项只知道它的下一项,加入我们需要一个树,它的项知道它的子项**和**父项。 + +让我们从一个叫做`Node`的存放拥有所有权的`i32`值和其子`Node`值的引用的结构体开始: + +```rust +use std::rc::Rc; +use std::cell::RefCell; + +#[derive(Debug)] +struct Node { + value: i32, + children: RefCell>>, +} +``` + +我们希望能够`Node`拥有其子节点,同时也希望变量可以拥有每个节点以便可以直接访问他们。这就是为什么`Vec`中的项是`Rc`值。我们也希望能够修改其他节点的子节点,这就是为什么`children`中`Vec`被放进了`RefCell`的原因。在列表 15-19 中创建了一个叫做`leaf`的带有值 3 并没有子节点的`Node`实例,和另一个带有值 5 和以`leaf`作为子节点的实例`branch`: + + +Filename: src/main.rs + +```rust,ignore +fn main() { + let leaf = Rc::new(Node { + value: 3, + children: RefCell::new(vec![]), + }); + + let branch = Rc::new(Node { + value: 5, + children: RefCell::new(vec![leaf.clone()]), + }); +} +``` + +Listing 15-19: Creating a `leaf` node and a `branch` node +where `branch` has `leaf` as one of its children but `leaf` has no reference to +`branch` + +`leaf`中的`Node`现在有两个所有者:`leaf`和`branch`,因为我们克隆了`leaf`中的`Rc`并储存在了`branch`中。`branch`中的`Node`知道它与`leaf`相关联因为`branch`在`branch.children`中有`leaf`的引用。然而,`leaf`并不知道它与`branch`相关联,而我们希望`leaf`知道`branch`是其父节点。 + +为了做到这一点,需要在`Node`结构体定义中增加一个`parent`字段,不过`parent`的类型应该是什么呢?我们知道它不能包含`Rc`,因为这样`leaf.parent`将会指向`branch`而`branch.children`会包含`leaf`的指针,这会形成引用循环。`leaf`和`branch`不会被丢弃因为他们总是引用对方且引用计数永远也不会是零。 + +所以在`parent`的类型中是使用`Weak`而不是`Rc`,具体来说是`RefCell>`: + +Filename: src/main.rs + +```rust +use std::rc::{Rc, Weak}; +use std::cell::RefCell; + +#[derive(Debug)] +struct Node { + value: i32, + parent: RefCell>, + children: RefCell>>, +} +``` + +这样,一个节点就能够在拥有父节点时指向它,而并不拥有其父节点。一个父节点哪怕在拥有指向它的子节点也会被丢弃,只要是其自身也没有一个父节点就行。现在将`main`函数更新为如列表 15-20 所示: + +Filename: src/main.rs + +```rust,ignore +fn main() { + let leaf = Rc::new(Node { + value: 3, + parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![]), + }); + + println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); + + let branch = Rc::new(Node { + value: 5, + parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![leaf.clone()]), + }); + + *leaf.parent.borrow_mut() = Rc::downgrade(&branch); + + println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); +} +``` + +Listing 15-20: A `leaf` node and a `branch` node where +`leaf` has a `Weak` reference to its parent, `branch` + +创建`leaf`节点是类似的;因为它作为开始并没有父节点,这里创建了一个新的`Weak`引用实例。当尝试通过`upgrade`方法获取`leaf`父节点的引用时,会得到一个`None`值,如第一个`println!`输出所示: + +```= +leaf parent = None +``` + +类似的,`branch`也有一个新的`Weak`引用,因为也没有父节点。`leaf`仍然作为`branch`的一个子节点。一旦在`branch`中有了一个新的`Node`实例,就可以修改`leaf`将一个`branch`的`Weak`引用作为其父节点。这里使用了`leaf`中`parent`字段里的`RefCell`的`borrow_mut`方法,接着使用了`Rc::downgrade`函数来从`branch`中的`Rc`值创建了一个指向`branch`的`Weak`引用。 + +当再次打印出`leaf`的父节点时,这一次将会得到存放了`branch`的`Some`值。另外需要注意到这里并没有打印出类似列表 15-14 中那样最终导致栈溢出的循环:`Weak`引用仅仅打印出`(Weak)`: + +``` +leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) }, +children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) }, +children: RefCell { value: [] } }] } }) +``` + +没有无限的输出(或直到栈溢出)的事实表明这里并没有引用循环。另一种证明的方式时观察调用`Rc::strong_count`和`Rc::weak_count`的值。在列表 15-21 中,创建了一个新的内部作用域并将`branch`的创建放入其中,这样可以观察`branch`被创建时和离开作用域被丢弃时发生了什么: + +Filename: src/main.rs + +```rust,ignore +fn main() { + let leaf = Rc::new(Node { + value: 3, + parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![]), + }); + + println!( + "leaf strong = {}, weak = {}", + Rc::strong_count(&leaf), + Rc::weak_count(&leaf), + ); + + { + let branch = Rc::new(Node { + value: 5, + parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![leaf.clone()]), + }); + *leaf.parent.borrow_mut() = Rc::downgrade(&branch); + + println!( + "branch strong = {}, weak = {}", + Rc::strong_count(&branch), + Rc::weak_count(&branch), + ); + + println!( + "leaf strong = {}, weak = {}", + Rc::strong_count(&leaf), + Rc::weak_count(&leaf), + ); + } + + println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); + println!( + "leaf strong = {}, weak = {}", + Rc::strong_count(&leaf), + Rc::weak_count(&leaf), + ); +} +``` + +Listing 15-21: Creating `branch` in an inner scope and +examining strong and weak reference counts of `leaf` and `branch` + +创建`leaf`之后,强引用计数是 1 (用于`leaf`自身)而弱引用计数是 0。在内部作用域中,在创建`branch`和关联`leaf`和`branch`之后,`branch`的强引用计数为 1(用于`branch`自身)而弱引用计数为 1(因为`leaf.parent`通过一个`Weak`指向`branch`)。`leaf`的强引用计数为 2,因为`branch`现在有一个`leaf`克隆的`Rc`储存在`branch.children`中。`leaf`的弱引用计数仍然为 0。 + +当内部作用域结束,`branch`离开作用域,其强引用计数减少为 0,所以其`Node`被丢弃。来自`leaf.parent`的弱引用计数 1 与`Node`是否被丢弃无关,所以并没有产生内存泄露! + +如果在内部作用域结束后尝试访问`leaf`的父节点,会像`leaf`拥有父节点之前一样得到`None`值。在程序的末尾,`leaf`的强引用计数为 1 而弱引用计数为 0,因为现在`leaf`又是唯一指向其自己的值了。 + +所有这些管理计数和值是否应该被丢弃的逻辑都通过`Rc`和`Weak`和他们的`Drop` trait 实现来控制。通过在定义中指定从子节点到父节点的关系为一个`Weak`引用,就能够拥有父节点和子节点之间的双向引用而不会造成引用循环和内存泄露。 + +## 总结 + +现在我们学习了如何选择不同类型的智能指针来选择不同的保证并与 Rust 的常规引用向取舍。`Box`有一个已知的大小并指向分配在堆上的数据。`Rc`记录了堆上数据的引用数量这样就可以拥有多个所有者。`RefCell`和其内部可变性使其可以用于需要不可变类型,但希望在运行时而不是编译时检查借用规则的场景。 + +我们还介绍了提供了很多智能指针功能的 trait `Deref`和`Drop`。同时探索了形成引用循环和造成内存泄漏的可能性,以及如何使用`Weak`避免引用循环。 + +如果本章内容引起了你的兴趣并希望现在就实现你自己的智能指针的话,请阅读 [The Nomicon] 来获取更多有用的信息。 + +[The Nomicon]: https://doc.rust-lang.org/stable/nomicon/vec.html + +接下来,让我们谈谈 Rust 的并发。我们还会学习到一些新的堆并发有帮助的智能指针。 \ No newline at end of file diff --git a/src/ch16-00-concurrency.md b/src/ch16-00-concurrency.md new file mode 100644 index 0000000..6813ca3 --- /dev/null +++ b/src/ch16-00-concurrency.md @@ -0,0 +1,21 @@ +# 无畏并发 + +> [ch16-00-concurrency.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch16-00-concurrency.md) +>
+> commit da15de39eaabd50100d6fa662c653169254d9175 + +确保内存安全并不是 Rust 的唯一目标:作为一个能更好的处理并发和并行编程一直是 Rust 的另一个主要目标。 +**并发编程**(concurrent programming)代表程序的不同部分相互独立的执行,而**并行编程**代表程序不同部分同时执行,这两个概念在计算机拥有更多处理器可供程序利用时变得更加重要。由于历史的原因,在此类上下文中编程一直是困难且容易出错的:Rust 希望能改变这一点。 + +最开始,我们认为内存安全和防止并发问题是需要通过两个不同的方法解决的两个相互独立的挑战。然而,随着时间的推移,我们发现所有权和类型系统是一系列解决内存安全**和**并发问题的强用力的工具!通过改进所有权和类型检查,很多并发错误在 Rust 中都是**编译时**错误,而不是运行时错误。我们给 Rust 的这一部分起了一个绰号**无畏并发**(*fearless concurrency*)。无畏并发意味着 Rust 不光允许你自信代码不会出现诡异的错误,也让你可以轻易重构这种代码而无需担心会引入新的 bug。 + +> 注意:对于 Rust 的口号**无畏并发**,这里用**并发**指代很多问题而不是更精确的区分**并发和(或)并行**,是处于简化问题的原因。如果这是一本专注于并发和/或并行的书,我们肯定会更精确的。对于本章,请自行脑补任何**并发**为**并发和(或)并行**。 + +很多语言对于其所提供的处理并发并发问题的解决方法是非常固执己见的。这是一个非常合理的策略,尤其是对于更高级的语言来说,不过对于底层语言来说可没有奢侈的选择。底层语言被期望为能在任何给定的场景中启用提供最高性能的方法,同时他们对硬件有更少的抽象。因此,Rust 给了我们多种工具来以适合场景和要求的方式来为问题建模。 + +如下是本章将要涉及到的内容: + +* 如果创建线程来同时运行多段代码。 +* 并发**消息传递**(*Message passing*),其中通道(channel)被用来在线程间传递消息。 +* 并发**共享状态**(*Shared state*),其中多个线程可以访问同一片数据。 +* `Sync`和`Send` trait,他们允许 Rust 的并发保证能扩展到用户定义的和标准库中提供的类型中。 \ No newline at end of file diff --git a/src/ch16-01-threads.md b/src/ch16-01-threads.md new file mode 100644 index 0000000..1cdd62c --- /dev/null +++ b/src/ch16-01-threads.md @@ -0,0 +1,11 @@ +## 使用线程同时运行代码 + +> [ch16-01-threads.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch16-01-threads.md) +>
+> commit 55b294f20fc846a13a9be623bf322d8b364cee77 + +在今天使用的大部分操作系统中,当程序执行时,操作系统运行代码的上下文称为**进程**(*process*)。操作系统可以运行很多进程,而操作系统也管理这些进程使得多个程序可以在电脑上同时运行。 + +我们可以将每个进程运行一个程序的概念再往下抽象一层:程序也可以在其上下文中同时运行独立的部分。这个功能叫做**线程**(*thread*)。 + +将程序需要执行的计算拆分到多个线程中可以提高性能,因为程序可以在同时进行很多工作。 \ No newline at end of file diff --git a/src/ch16-02-message-passing.md b/src/ch16-02-message-passing.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ch16-03-shared-state.md b/src/ch16-03-shared-state.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ch16-04-extensible-concurrency-sync-and-send.md b/src/ch16-04-extensible-concurrency-sync-and-send.md new file mode 100644 index 0000000..e69de29