trpl-zh-cn/ch17-06-futures-tasks-threads.html
2025-05-06 11:01:09 +00:00

301 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Futures任务tasks和线程threads - Rust 程序设计语言 简体中文版</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="ferris.css">
<link rel="stylesheet" href="theme/2018-edition.css">
<link rel="stylesheet" href="theme/semantic-notes.css">
<link rel="stylesheet" href="theme/listing.css">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Start loading toc.js asap -->
<script src="toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust 程序设计语言 简体中文版</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/KaiserY/trpl-zh-cn/tree/main" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/KaiserY/trpl-zh-cn/edit/main/src/ch17-06-futures-tasks-threads.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="结合使用-future任务和线程"><a class="header" href="#结合使用-future任务和线程">结合使用 future、任务和线程</a></h2>
<blockquote>
<p><a href="https://github.com/rust-lang/book/blob/main/src/ch17-06-futures-tasks-threads.md">ch17-06-futures-tasks-threads.md</a>
<br>
commit 06d73f3935dfec895aec9790127dc8b6fc827ce1</p>
</blockquote>
<p>正如我们在<a href="ch16-00-concurrency.html">第十六章</a>所见,线程提供了一种并发的方式。在这一章节我们见过了另一种方式:通过 future 和流来使用异步。如果你好奇何时选择一个而不是另一个,答案是:因情况而异!同时在很多场景下,选择不是线程<strong></strong>异步而是线程<strong></strong>异步。</p>
<p>几十年来很多操作系统已经提供了基于线程的并发模型,因此很多编程语言也支持它们。然而这些模型并非没有取舍。在很多操作系统中,它们为每一个线程使用了不少的内存,同时启动和停止带来了一些开销。线程也只有当你的操作系统和硬件支持它们的时候才是一个选项。不同于主流的台式机和移动电脑,一些嵌入式系统完全没有操作系统,所以它们也没有线程。</p>
<p>异步模型提供了一个不同的 -- 最终也是互补的 -- 权衡取舍。在异步模型中,并发操作并不需求自己的线程。相反,它们运行在任务上,正如流小节中我们用 <code>trpl::spawn_task</code> 从异步函数中开始工作一样。任务类似于线程,不过不同于由操作系统管理,它由库级别的代码管理:也就是运行时。</p>
<p>在上一小节,我们看到可以通过异步信道来构建一个流并产生一个可以在异步代码中调用的异步任务。我们也可以用线程来做到完全相同的事情。在示例 17-40 中使用了 <code>trpl::spawn_task</code><code>trpl::sleep</code>。在示例 17-41 中,我们将 <code>get_intervals</code> 函数中的代码替换为标准库中的 <code>thread::spawn</code><code>thread::sleep</code> API。</p>
<figure class="listing">
<p><span class="file-name">文件名src/main.rs</span></p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">extern crate trpl; // required for mdbook test
</span><span class="boring">
</span><span class="boring">use std::{pin::pin, thread, time::Duration};
</span><span class="boring">
</span><span class="boring">use trpl::{ReceiverStream, Stream, StreamExt};
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> trpl::run(async {
</span><span class="boring"> let messages = get_messages().timeout(Duration::from_millis(200));
</span><span class="boring"> let intervals = get_intervals()
</span><span class="boring"> .map(|count| format!("Interval #{count}"))
</span><span class="boring"> .throttle(Duration::from_millis(500))
</span><span class="boring"> .timeout(Duration::from_secs(10));
</span><span class="boring"> let merged = messages.merge(intervals).take(20);
</span><span class="boring"> let mut stream = pin!(merged);
</span><span class="boring">
</span><span class="boring"> while let Some(result) = stream.next().await {
</span><span class="boring"> match result {
</span><span class="boring"> Ok(item) =&gt; println!("{item}"),
</span><span class="boring"> Err(reason) =&gt; eprintln!("Problem: {reason:?}"),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring"> });
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn get_messages() -&gt; impl Stream&lt;Item = String&gt; {
</span><span class="boring"> let (tx, rx) = trpl::channel();
</span><span class="boring">
</span><span class="boring"> trpl::spawn_task(async move {
</span><span class="boring"> let messages = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
</span><span class="boring">
</span><span class="boring"> for (index, message) in messages.into_iter().enumerate() {
</span><span class="boring"> let time_to_sleep = if index % 2 == 0 { 100 } else { 300 };
</span><span class="boring"> trpl::sleep(Duration::from_millis(time_to_sleep)).await;
</span><span class="boring">
</span><span class="boring"> if let Err(send_error) = tx.send(format!("Message: '{message}'")) {
</span><span class="boring"> eprintln!("Cannot send message '{message}': {send_error}");
</span><span class="boring"> break;
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring"> });
</span><span class="boring">
</span><span class="boring"> ReceiverStream::new(rx)
</span><span class="boring">}
</span><span class="boring">
</span>fn get_intervals() -&gt; impl Stream&lt;Item = u32&gt; {
let (tx, rx) = trpl::channel();
// This is *not* `trpl::spawn` but `std::thread::spawn`!
thread::spawn(move || {
let mut count = 0;
loop {
// Likewise, this is *not* `trpl::sleep` but `std::thread::sleep`!
thread::sleep(Duration::from_millis(1));
count += 1;
if let Err(send_error) = tx.send(count) {
eprintln!("Could not send interval {count}: {send_error}");
break;
};
}
});
ReceiverStream::new(rx)
}</code></pre></pre>
<figcaption>示例 17-41为 `get_intervals` 函数使用 `std::thread` API 而不是异步 `trpl` API</figcaption>
</figure>
<p>如果你运行这段代码,其输入与示例 17-40 的一样。并且请注意从调用代码的角度来说改变是多么的微小。而且,即便一个函数在运行时上产生一个异步任务而另一个产生一个系统线程,其返回的流不受该区别的影响。</p>
<p>尽管它们是相似的,这两种方式的行为非常不同,尽管在这个非常简单的例子中我们可能很难进行测量。我们可以在任意现代计算机中产生数以百万计的异步任务。如果尝试用线程来这样做,我们实际上会耗尽内存!</p>
<p>然而,它们的 API 如此相似是有理由的。线程作为同步操作集的边界;线程<strong>之间</strong>的并发是可能的。任务作为<strong>异步</strong>操作集的边界,任务<strong>之间</strong><strong>之内</strong>的并发是可能的,因为任务可以在其内部切换 future。最后future 是 Rust 中最细粒度的并发单位,同时每一个 future 可能代表一个其它 future 的数。其运行时 -- 更准确地说其执行器executor-- 管理任务,任务则管理 future。在这一点上任务类似于轻量的、运行时管理的线程并带有额外的由运行时管理而不是操作系统管理的能力。</p>
<p>这并不意味着异步任务总是优于线程(或者相反)。基于线程的并发在某种程度上来说是一个比基于 <code>async</code> 的并发更简单的编程模型。这可以是一个优点也可以是一个缺点。线程有点像 “射后不理”“fire and forget”</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch17-05-traits-for-async.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="ch18-00-oop.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="ch17-05-traits-for-async.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="ch18-00-oop.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js"></script>
<script src="mark.min.js"></script>
<script src="searcher.js"></script>
<script src="clipboard.min.js"></script>
<script src="highlight.js"></script>
<script src="book.js"></script>
<!-- Custom JS scripts -->
<script src="ferris.js"></script>
</div>
</body>
</html>