rust进阶.并发.Tokio.1.Tokio简介
<p>学习要,工作也不能拉下,所以这一段时间关于rust的博文少了些。</p><p>rust要学习的内容还很多,但我觉得应该优先打好基础,这其中比较关注的是并发。</p>
<p>提到rust的并发,先回忆在书本<<rust编程语言>>有许多的内容:</p>
<p><strong>1.并发和并行</strong></p>
<p><strong>2.通过信道(channel)共享进程间数据</strong></p>
<p>关键库和方法</p>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, "Courier New", monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre"><span style="color: rgba(0, 0, 0, 1)">std</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">sync</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">mpsc</span></div>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, "Courier New", monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre"><span style="color: rgba(0, 0, 0, 1)">mpsc</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">channel()</span>
<div>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, "Courier New", monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre"><span style="color: rgba(0, 0, 0, 1)">std</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">thread</span>
<div>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, "Courier New", monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre"><span style="color: rgba(0, 0, 0, 1)">thread</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">spawn(</span>)
<div> </div>
<div><strong><span style="white-space: normal">3.通过内存共享进程间数据</span></strong></div>
</div>
</div>
</div>
</div>
</div>
<p>关键库和方法</p>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, "Courier New", monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre"><span style="color: rgba(0, 0, 0, 1)">std</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">sync</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">{Arc, Mutex}</span></div>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, "Courier New", monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre"><span style="color: rgba(0, 0, 0, 1)">std::thread</span>
<div><span style="color: rgba(0, 0, 0, 1)">std::thread</span><span style="color: rgba(0, 0, 0, 1)">::</span><span style="color: rgba(0, 0, 0, 1)">JoinHandle</span></div>
<div><span style="color: rgba(0, 0, 0, 1)">JoinHandle::join()</span>
<div> </div>
<div><strong><span style="color: rgba(0, 0, 0, 1)">4.Send和Sync特质</span></strong></div>
<div><span style="color: rgba(0, 0, 0, 1)">Send 标记 trait 表明实现了 Send 的类型值的所有权可以在线程间传送。几乎所有的 Rust 类型都是Send 的,<br> * 不过有一些例外,包括 Rc<T>,这是因为Rc的fetch_add(引用计数)属于非原子操作</span></div>
<div><span style="color: rgba(0, 0, 0, 1)">Sync 标记 trait 表明一个实现了 Sync 的类型可以安全的在多个线程中拥有其值的引用</span></div>
</div>
</div>
<p><span style="color: rgba(0, 0, 0, 1); font-family: Consolas, "Courier New", monospace; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0; text-transform: none; widows: 2; word-spacing: 0; -webkit-text-stroke-width: 0px; white-space: pre-wrap; background-color: rgba(243, 243, 243, 1); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none">5.core::future::future --未来值</span></p>
<p> * 1.future 是一个现在可能还没有准备好但将在未来某个时刻准备好的值<br> * 2.Rust 提供了 Future trait 作为基础组件,这样不同的异步操作就可以在不同的数据结构上实现<br> * 3.每一个实现了 Future 的类型会维护自己的进度状态信息和 “ready” 的定义<br> * 4.async 关键字可以用于代码块和函数<br> * 5.在一个 async 块或 async 函数中,可以使用 await 关键字来等待一个 future 准备就绪,这一过程称为 等待一个 future<br> * 6.检查一个 future 并查看其值是否已经准备就绪的过程被称为 轮询(polling)<br> * 7.在大多数情况下,编写异步 Rust 代码时,我们使用 async 和 await 关键字。<br> * Rust 将其编译为等同于使用 Future trait 的代码,这非常类似于将 for 循环编译为等同于使用 Iterator trait 的代码</p>
<p> </p>
<p>虽然rust的标准库已经可以解决并发的问题,但是异步操作据说还是需要依赖于第三方的运行时来进行,这其中有几个可以选择:</p>
<table class="OxEWNITQ IpLYzD4O">
<thead>
<tr>
<th>运行时库</th>
<th>异步操作支持</th>
<th>Future 支持</th>
<th>性能表现</th>
<th>易用性</th>
<th>生态兼容性</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Tokio</strong></td>
<td>多线程工作窃取调度器,支持百万级并发连接</td>
<td>丰富组合子(<code class=" inline">join!</code>, <code class=" inline">select!</code>),兼容 <code class=" inline">async-std</code></td>
<td>★★★★★(行业标杆)</td>
<td>★★★☆(中等学习曲线)</td>
<td>★★★★★(Hyper/Tonic 等主流框架)</td>
<td>高性能网络服务(API 网关、实时通信)</td>
</tr>
<tr>
<td><strong>async-std</strong></td>
<td>类似标准库的异步 API(<code class=" inline">async_std::fs</code> 等)</td>
<td>与 <code class=" inline">std::future</code> 完全兼容,支持 <code class=" inline">Futures Unordered</code></td>
<td>★★★☆(轻量高效)</td>
<td>★★★★★(零学习成本)</td>
<td>★★★☆(基础工具链完善)</td>
<td>快速原型开发、CLI 工具、轻量 Web 服务</td>
</tr>
<tr>
<td><strong>Smol</strong></td>
<td>轻量级任务模型(类似 goroutine)</td>
<td>模块化设计,可通过 <code class=" inline">async-channel</code> 扩展</td>
<td>★★★★☆(低资源占用)</td>
<td>★★★★☆(简洁 API)</td>
<td>★★☆(需依赖 <code class=" inline">async-executors</code>)</td>
<td>微服务、IoT 设备、极简依赖链项目</td>
</tr>
<tr>
<td><strong>Glommio</strong></td>
<td>基于 <code class=" inline">io_uring</code> 的线程亲和性调度器</td>
<td>实验性 Future API,支持 <code class=" inline">FutureExt</code> 扩展</td>
<td>★★☆(延迟波动)</td>
<td>★☆(底层 API)</td>
<td>★☆(无成熟 HTTP 库)</td>
<td>研究性项目、探索新技术边界</td>
</tr>
<tr>
<td><strong>rustasync/runtime</strong></td>
<td>底层抽象,支持自定义线程池/调度策略</td>
<td>灵活但复杂,需直接操作 <code class=" inline">Runtime::builder()</code></td>
<td>★★★☆(可调优)</td>
<td>★☆(高级开发者专用)</td>
<td>★★☆(需自行集成生态)</td>
<td>深度定制需求、自定义异步框架</td>
</tr>
</tbody>
</table>
<p>从上表可以看出,tokio相对比较突出,尤其是性能和生态方面,而这时应用开发所需要关注的。</p>
<p> </p>
<h1>一、tokio发展历史</h1>
<p id="">Tokio 的诞生与 Rust 异步生态的演进紧密相连:</p>
<ol>
<li>
<p id=""><strong>起源与整合(2016年)</strong><br>Tokio 最初是 <code class=" inline">futures</code>(异步操作基本 API)和 <code class=" inline">mio</code>(跨平台非阻塞 IO 库)的整合库,名为 <code class=" inline">futures_mio</code>。2016 年 8 月更名为 <strong>Tokio</strong>,计划构建一整套异步设施,核心组件包括 <code class=" inline">tokio-core</code>(单线程模型)、<code class=" inline">tokio-io</code>、<code class=" inline">tokio-timer</code> 等。</p>
</li>
<li>
<p id=""><strong>性能与易用性优化</strong></p>
<ul>
<li>引入宏实现 <code class=" inline">async/await</code> 语法(如 <code class=" inline">futures-await</code> 库),提升异步代码的可读性。</li>
<li>通过 <code class=" inline">tokio-proto</code> 等模块提供上层协议支持,但后续因易用性问题被逐步优化。</li>
</ul>
</li>
<li>
<p id=""><strong>生态定位与标准化(2018年)</strong><br>Rust 官方成立 <code class=" inline">net-wg</code> 小组,将 <code class=" inline">futures</code> 工作移交,并倡导“中立的 <code class=" inline">futures</code> 提供基础能力,Tokio 专注 IO 相关接口”的设计。Tokio 虽未完全采纳此方案,但持续演进,成为事实标准的异步运行时。</p>
</li>
<li>
<p id=""><strong>现状与影响</strong><br>Tokio 凭借高性能(如支持百万级并发连接)、丰富工具链(异步文件/网络/定时器)和生态整合(如 Hyper、Axum 框架),成为 Rust 异步开发的首选方案。</p>
</li>
</ol>
<h3>命名由来:为什么叫 Tokio?</h3>
<p id="">Tokio 的命名灵感来源于 <strong>“Tokyo(东京)+ IO”</strong> 的组合,寓意:</p>
<ul>
<li><strong>高效与繁忙</strong>:东京作为国际化大都市,象征着高效处理海量任务的能力,与 Tokio 处理异步 IO 的目标契合。</li>
<li><strong>技术愿景</strong>:项目初期目标是构建像东京都市圈一样“繁忙却高效”的异步运行时。</li>
</ul>
<h3>Tokio 的字面意思</h3>
<ul>
<li><strong>日语语境</strong>:Tokio 在日语中<strong>没有实际含义</strong>,它是“东京”的西班牙语拼写变体。日语中“东京”的标准表达为平假名「とうきょう」或汉字「東京」。</li>
<li><strong>语言差异</strong>:西班牙语采用“Tokio”拼写东京,源于其语音规则(如保留词尾 <code class=" inline">-o</code>),而日语官方罗马字转写为“Tokyo”。Tokio 这一拼写主要出现在西班牙语系国家或国际交流场景中。</li>
</ul>
<h3>总结</h3>
<p id="">Tokio 的命名既体现了技术愿景(高效处理 IO),也蕴含了文化隐喻(东京的国际化形象)。其发展历程反映了 Rust 异步生态从底层整合到标准化、易用化的演进,最终成为高性能异步开发的基石。</p>
<h1>二、tokio官方文档概要</h1>
<p>以下内容来自tokio的文档。</p>
<p>通过: cargo doc --open --package tokio 能打开英文版本。</p>
<p>以下内容是对这个cargo doc的翻译。</p>
<p> </p>
<h2>2.1、tokio旅程</h2>
<h3>编写应用</h3>
<p>tokio很适合用于编写应用,在这种情况下使用人不需要担忧所需要采用的tokio特性。</p>
<p>如果您不确定,那么我们建议你使用“full"以确保在构建应用过程中不会有什么阻滞。(是的,在这个年代磁盘已经不经不值钱了)</p>
<p>以下代码会安装tokio的所有模块:</p>
<p><code>tokio = { version = "1", features = ["full"] }</code></p>
<p> </p>
<h3>2.1.1、编写库</h3>
<p>如果是编写库,自然以提供最轻便的单元包为目标。为了达成这个目标,你应该确保只使用了你需要的特性(模块)。如此,库的用户就可以不要启用一些无用的特性。</p>
<p>例如:</p>
<p><code>tokio = { version = "1", features = ["rt", "net"] }</code></p>
<p> </p>
<h3>2.1.2、使用任务(task)</h3>
<p>rust的异步编程基于轻量、非阻塞的执行单元,这些执行单元称为tasks.</p>
<p>tokio::<strong>task</strong>模块提供了重要的工具一些和tasks一起工作:</p>
<ul>
<li>spawn函数和JoinHandle类型-前者负责基于tokio运行时调度一个新任务,后者则等待任务的输出</li>
<li>用于异步任务中运行阻塞操作的函数</li>
</ul>
<p>tokio::task模块在特性"rt"启用的时候才会提供。</p>
<p>tokio::<strong>sync</strong>包含了同步的功能,以便在需要的时候交流或者共享数据。这些功能包括:</p>
<ul>
<li>通道(oneshot,mpsc,watch和broadcast),用于在任务之间发送值</li>
<li>非阻塞Mutex(互斥锁),用于控制对一个共享可变值得存取</li>
<li>一个异步Barrier类型(屏障),用于多个任务在开始计算前进行同步</li>
</ul>
<p>tokio::sync只有特性"sync"启用得时候才会提供。</p>
<p> </p>
<p>tokio::<strong>time</strong>模块提供了用于追踪时间和调度工作的许多工具。包括设置任务的超时(timeouts),休眠(sleeping)将来要运行的工作,或者</p>
<p>以特定的间隔重复操作。</p>
<p>为了使用tokio:time,必须启用"time"特性。</p>
<p> </p>
<p><strong>最后,tokio提供了执行异步任务的运行时。大部分应用可以使用#宏,这样应用就可以基于tokio运行时运行</strong>。</p>
<p>然而,这个宏仅仅提供了基本的配置项。 作为一个替代,tokio:runtime模块提供了更多的强大API,这些api能够配置和管理运行时。</p>
<p>如果#宏无法满足需要,就应该使用这些api。</p>
<p> </p>
<p>要使用这个运行时,必须启用"rt"或者"rt-multi-thread"特性,这样才可以分别开启当前线程的single-threaded scheduler(单线程调度器)和multi-thread scheduler(多线程调度器)。</p>
<p>runtime module documentation提供了更多的细节。 </p>
<p>此外,特性"macros"启用了#和#属性。</p>
<p> </p>
<p> </p>
<h3>2.1.3、cpu绑定任务和阻塞代码</h3>
<p>tokio能够基于一些线程同时运行许多任务(线程池),方式是重复唤起每个线程当前运行任务。</p>
<p>然而,这种唤起只能基于.await关键点,因此耗费较长运行时间的代码,如果没有遇到遇到.await,那么它们会阻止其它任务的运行。为了解决这个问题,tokio提供了两种线程:核心线程和阻塞线程。</p>
<p> </p>
<p>核心线程用于运行异步代码,tokio默认为一个cpu内核唤起一个内核线程。但我们可以使用环境变量TOKIO_WORKER_THREADS覆盖默认值。</p>
<p> </p>
<p>阻塞线程则是根据需要唤起,能用于运行阻塞代码。这些代码会阻止其它任务运行,并让它自己保持活跃(即使一段时间没有用),这个保持活跃的功能可以通过thread_keep_alive进行配置。</p>
<p>由于tokio无法换出一个阻塞的任务,就像它可以和异步代码一起工作(?),阻塞线程数上限非常大。这个上限可以同故宫Builder进行配置。</p>
<p>为了唤起一个阻塞任务,我们应该使用spawn_blocking函数。</p>
<pre class="highlighter-prismjs prismjs-lines-highlighted language-rust" tabindex="0"><code>#
async fn main() {
// This is running on a core thread.
let blocking_task = tokio::task::spawn_blocking(|| {
// This is running on a blocking thread.
// Blocking here is ok.
});
// We can wait for the blocking task like this:
// If the blocking task panics, the unwrap below will propagate the
// panic.
blocking_task.await.unwrap();
}</code></pre>
<p> </p>
<p>如果我们的代码和cpu密切关联,那么我们应该会希望限制cpu上运行的线程数。因此我们应该用一个单独的线程池来处理和cpu关系密切的任务。例如,我们可以考虑使用rayon库。它也能创建额外的tokio运行时,用于处理cpu关系密切的任务,但是如果我们这么做,就应该谨慎一些,因为这些额外的运行时候只运行cpu密切的任务,如果运行IO密切的任务,那么表现不理想。</p>
<p>提示:如果使用rayon,我们可以创建一个通道,当rayon任务完成时,用于把结果发送回tokio。</p>
<p> </p>
<h3>2.1.4、异步IO</h3>
<p>tokio能够调度和运行任务,而且也提供用于异步处理io的每一样东西。</p>
<p>tokio::io模块提供了tokio异步核心io功能,包括AsyncRead,AsyncWrite,AsyncBufRead特质。</p>
<p>此外,当启用了"io-util"特性后,tokio也提供了和这些特质相关的组合体和方法,并构建一个异步的组件给std::io。</p>
<p> </p>
<p>tokio也包含了执行各种io的api,api和操作系统做异步的交互,它们包括:</p>
<ul>
<li>tokio::net-包含了非阻塞版本的TCP,UDP和UNIX Domain Socket(要求启用net特新)</li>
<li>tokio::fs -类似std::fs,用于异步处理文件系统io,要求启用特性fs</li>
<li>tokio::signal-用于异步处理unix和windows的型号,要求启用signal特性</li>
<li>tokio::process-用于唤起和管理字进程,要求启用process特性</li>
</ul>
<p> </p>
<h2>2.2、一个简单的示例</h2>
<pre class="highlighter-prismjs prismjs-lines-highlighted language-rust" tabindex="0"><code>use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = ;
// In a loop, read data from the socket and write the data back.
loop {
let n = match socket.read(&mut buf).await {
// socket closed
Ok(0) => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the data back
if let Err(e) = socket.write_all(&buf).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}</code></pre>
<p> </p>
<p>这个例子使用的tokio的异步io,包括net和io。</p>
<p>每当接到一个socket连接,就回启动一个任务,这个任务回读取并写回内容到socket中。</p>
<p> </p>
<h2>2.3、特征/特性标记</h2>
<p>分为不可靠和可靠两个部分。</p>
<p>注意,当前文章基于tokio的1.45.0版本。</p>
<table style="border-collapse: collapse; width: 100%; height: 336px" border="1">
<tbody>
<tr style="height: 21px; background-color: rgba(206, 212, 217, 1)">
<td style="width: 16.1692%; height: 21px">编码</td>
<td style="width: 13.3047%; height: 21px">名称</td>
<td style="width: 61.6953%; height: 21px">启用说明</td>
<td style="width: 8.83085%; height: 21px">是否稳定</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">full</td>
<td style="width: 13.3047%; height: 21px">所有</td>
<td style="width: 61.6953%; height: 21px">全部</td>
<td style="width: 8.83085%; height: 21px"> </td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px"><strong>rt</strong></td>
<td style="width: 13.3047%; height: 21px">运行时</td>
<td style="width: 61.6953%; height: 21px">tokio::spawn,当前线程调度器和非调度器工具</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">rt-multi-thread</td>
<td style="width: 13.3047%; height: 21px">多线程运行时</td>
<td style="width: 61.6953%; height: 21px">更重的多线程,工作窃取调度器(work-stealing scheduler)?</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">io-util</td>
<td style="width: 13.3047%; height: 21px">io工具</td>
<td style="width: 61.6953%; height: 21px">基于IO的Ext特质等</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">io-std</td>
<td style="width: 13.3047%; height: 21px">标准IO</td>
<td style="width: 61.6953%; height: 21px">Stdout,Stdin和Stderr类型</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 42px">
<td style="width: 16.1692%; height: 42px"><strong>net</strong></td>
<td style="width: 13.3047%; height: 42px">网络</td>
<td style="width: 61.6953%; height: 42px">tokio::net类型(TpcStream,UnixStream,UdpSocket),还有AsyncFd(Unix类系统,可用于linux)和PollAio(FreeBsd)</td>
<td style="width: 8.83085%; height: 42px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px"><strong>time</strong></td>
<td style="width: 13.3047%; height: 21px">时间</td>
<td style="width: 61.6953%; height: 21px">tokio::time类型,计时器</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">process</td>
<td style="width: 13.3047%; height: 21px">进程</td>
<td style="width: 61.6953%; height: 21px">tokio::proecss类型</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">macros</td>
<td style="width: 13.3047%; height: 21px">宏</td>
<td style="width: 61.6953%; height: 21px">tokio::main和tokio::test宏</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px"><strong>sync</strong></td>
<td style="width: 13.3047%; height: 21px">同步</td>
<td style="width: 61.6953%; height: 21px">tokio:sync类型</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">signal</td>
<td style="width: 13.3047%; height: 21px">信号</td>
<td style="width: 61.6953%; height: 21px">tokio::signal类型</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px"><strong>fs</strong></td>
<td style="width: 13.3047%; height: 21px">文件系统</td>
<td style="width: 61.6953%; height: 21px">tokio::fs类型</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">test-util</td>
<td style="width: 13.3047%; height: 21px">测试工具</td>
<td style="width: 61.6953%; height: 21px">tokio运行时测试框架</td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr style="height: 21px">
<td style="width: 16.1692%; height: 21px">parking_lot</td>
<td style="width: 13.3047%; height: 21px">特车位?</td>
<td style="width: 61.6953%; height: 21px"> </td>
<td style="width: 8.83085%; height: 21px">✔</td>
</tr>
<tr>
<td style="width: 16.1692%">tracing</td>
<td style="width: 13.3047%">追踪</td>
<td style="width: 61.6953%">要求启用构建标记tokio_unstable</td>
<td style="width: 8.83085%">❌</td>
</tr>
<tr>
<td style="width: 16.1692%">其它</td>
<td style="width: 13.3047%"> </td>
<td style="width: 61.6953%">
<ul>
<li>[<code>task::Builder</code>]</li>
<li>Some methods on <code>task::JoinSet</code></li>
<li><code>runtime::RuntimeMetrics</code></li>
<li>[<code>runtime::Builder::on_task_spawn</code>]</li>
<li>[<code>runtime::Builder::on_task_terminate</code>]</li>
<li>[<code>runtime::Builder::unhandled_panic</code>]</li>
<li>[<code>runtime::TaskMeta</code>]</li>
</ul>
<p>要求启用构建标记tokio_unstable</p>
</td>
<td style="width: 8.83085%">❌</td>
</tr>
</tbody>
</table>
<p>特别注意:<em><code>AsyncRead</code> and <code>AsyncWrite 这两个特质总是可用,即使没有申明(即这是最基本的部分)</code></em></p>
<p> </p>
<p><strong>如何在构建的时候设置tokio_unstable</strong></p>
<p>在项目的 .cargo/config.toml中配置</p>
<pre class="highlighter-prismjs language-plaintext prismjs-lines-highlighted" tabindex="0"><code>
rustflags = ["--cfg", "tokio_unstable"]</code></pre>
<p>此外,通过环境变量配置也可以。</p>
<p>windows</p>
<pre class="highlighter-prismjs language-plaintext prismjs-lines-highlighted" tabindex="0"><code>$Env:RUSTFLAGS="--cfg tokio_unstable"</code></pre>
<p>linux</p>
<pre class="highlighter-prismjs language-plaintext prismjs-lines-highlighted" tabindex="0"><code>export RUSTFLAGS="--cfg tokio_unstable"</code></pre>
<p> </p>
<h2>2.4、支持的平台</h2>
<p>tokio目前支持以下平台:</p>
<ul>
<li>Linux</li>
<li>Windows</li>
<li>Andriod(API 级别21)</li>
<li>macOS</li>
<li>iOS</li>
<li>FreeBs</li>
</ul>
<p>未来,tokio还会支持这些平台。然而,将来的版本可能会调整要求,这些要求包括libc版本,api级别,或者特定的FreeBSD版本。</p>
<p> </p>
<p>除了这些平台,tokio也会倾向于在mio包能够运行的平台上工作。 mio可以支持的平台参见:in mio’s documentation</p>
<p>然而,这些额外的平台,将来可能不被支持。</p>
<p>注意,Wine平台不同于windows。</p>
<h3>2.4.1、wasm支持</h3>
<p>tokio对于WASM平台的支持存在一些限制。</p>
<p>如果不启用tokio_unstable标记,那么可以支持以下特性:</p>
<ul>
<li>sync</li>
<li>macros</li>
<li>io-util</li>
<li>rt</li>
<li>time</li>
</ul>
<p>如果企图支持其它特性,那么会导致编译失败。</p>
<p>time模块只会在那些支持timers(例如wasm32-wasi)的wasm平台上工作。</p>
<p> </p>
<p>注意:如果运行时变得无限期空闲,那么它会立刻终止,而不是永久阻塞。 对于不支持time的平台,这意味着运行时任何时候都不会变得空闲。</p>
<p> </p>
<h3>2.4.2、不稳定的WASM支持</h3>
<p>tokio有可以不稳定地支持一些wasm特性。这种情况下要求启用tokio_unstable标记。</p>
<p>tokio::net可以支持wasm32-wasi。然而,不是所有方法都支持网络类型,当WASI不支持创建创建新的套接字的时候。</p>
<p>因此,套接字必须通过FromRawFd特质创建。</p>
<p> </p>
<h2>2.5、单元项目</h2>
<p> </p>
<h3>2.5.1、重新导出</h3>
<p>pub use task::spawn;</p>
<p> </p>
<h3>2.5.2、模块</h3>
<p>注:模块基本和特性对应</p>
<table style="border-collapse: collapse; width: 100.022%" border="1">
<tbody>
<tr>
<td style="width: 11.568%">编码</td>
<td style="width: 20.4511%">名称</td>
<td style="width: 67.9922%"> 说明</td>
</tr>
<tr>
<td style="width: 11.568%">fs</td>
<td style="width: 20.4511%">文件</td>
<td style="width: 67.9922%">异步文件工具</td>
</tr>
<tr>
<td style="width: 11.568%">io</td>
<td style="width: 20.4511%">io</td>
<td style="width: 67.9922%">异步io中的特质,助手,和定义</td>
</tr>
<tr>
<td style="width: 11.568%">net</td>
<td style="width: 20.4511%">网络</td>
<td style="width: 67.9922%">TCP/UPD/Unix相关</td>
</tr>
<tr>
<td style="width: 11.568%">process</td>
<td style="width: 20.4511%">进程</td>
<td style="width: 67.9922%">异步进程管理</td>
</tr>
<tr>
<td style="width: 11.568%">runtime</td>
<td style="width: 20.4511%">运行时</td>
<td style="width: 67.9922%">tokio运行时</td>
</tr>
<tr>
<td style="width: 11.568%">signal</td>
<td style="width: 20.4511%">信号</td>
<td style="width: 67.9922%">异步信号处理</td>
</tr>
<tr>
<td style="width: 11.568%">stream</td>
<td style="width: 20.4511%">流</td>
<td style="width: 67.9922%">流相关</td>
</tr>
<tr>
<td style="width: 11.568%">sync</td>
<td style="width: 20.4511%">同步</td>
<td style="width: 67.9922%">异步上下文中同步操作</td>
</tr>
<tr>
<td style="width: 11.568%">task</td>
<td style="width: 20.4511%">任务</td>
<td style="width: 67.9922%">异步绿色线程</td>
</tr>
<tr>
<td style="width: 11.568%">time</td>
<td style="width: 20.4511%">时间</td>
<td style="width: 67.9922%">追踪时间的工具</td>
</tr>
</tbody>
</table>
<p> </p>
<h3>2.5.3、宏</h3>
<ol>
<li>join-等待并发的多个分支,当所有分支完成就会返回</li>
<li>pin-在栈上钉住一个值</li>
<li>select-等待多个并发的分支,当第一个任务完成,则返回,并放弃其它分支</li>
<li>task_local-定义一个新的本地键,类型是tokio::task::LocalKey</li>
<li>try_join-等待多个并发的分支,如果所有的成功Ok(_)则返回,如果发现一个Err()也会返回</li>
</ol>
<p> </p>
<h3>2.5.4、属性宏</h3>
<ol>
<li>main-标记选定的运行时执行的异步函数。这个宏可以配置一个运行时,而不要求用户使用Runtime或者Builder</li>
<li>test-类似main,但只用于测试环境。</li>
</ol>
<p> </p>
<h2>2.6、单元包</h2>
<p>相关单元包。</p>
<p>这个没有什么特别值得写得内容,罗列下:</p>
<p><strong>bytes </strong></p>
<p>顾名思义,和自己操作有关的,例如</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>use bytes::{BytesMut, BufMut};
let mut buf = BytesMut::with_capacity(1024);
buf.put(&b"hello world"[..]);
buf.put_u16(1234);
let a = buf.split();
assert_eq!(a, b"hello world\x04\xD2"[..]);
buf.put(&b"goodbye world"[..]);
let b = buf.split();
assert_eq!(b, b"goodbye world"[..]);
assert_eq!(buf.capacity(), 998);</code></pre>
<p>bytes主要用于和网络操作有关的场景。 tokio利用了零拷贝的编程。</p>
<p>可以使用rust已有的类型来创建字节数组,例如&[]或者Vev<u8>,但推荐使用Bytes</p>
<p> </p>
<p><strong>cfg_if</strong></p>
<p>是一个配置有关的宏,构建类似if/else的代码结构。</p>
<p>tokio由于需要需要兼顾多个平台,不可避免要存在不少的if/else.</p>
<p>示例:</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>cfg_if::cfg_if! {
if # {
fn foo() { /* unix specific functionality */ }
} else if # {
fn foo() { /* non-unix, 32-bit functionality */ }
} else {
fn foo() { /* fallback implementation */ }
}
}</code></pre>
<p> </p>
<p><strong>lock_api</strong></p>
<p>和锁相关的api。</p>
<p>大体可以看作是多标准rust锁有关类型的封装.好处在于省掉了不少繁复的操作,例如:</p>
<p>示例:</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>use lock_api::{RawMutex, Mutex, GuardSend};
use std::sync::atomic::{AtomicBool, Ordering};
// 1. Define our raw lock type
pub struct RawSpinlock(AtomicBool);
// 2. Implement RawMutex for this type
unsafe impl RawMutex for RawSpinlock {
const INIT: RawSpinlock = RawSpinlock(AtomicBool::new(false));
// A spinlock guard can be sent to another thread and unlocked there
type GuardMarker = GuardSend;
fn lock(&self) {
// Note: This isn't the best way of implementing a spinlock, but it
// suffices for the sake of this example.
while !self.try_lock() {}
}
fn try_lock(&self) -> bool {
self.0
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}
unsafe fn unlock(&self) {
self.0.store(false, Ordering::Release);
}
}
// 3. Export the wrappers. This are the types that your users will actually use.
pub type Spinlock<T> = lock_api::Mutex<RawSpinlock, T>;
pub type SpinlockGuard<'a, T> = lock_api::MutexGuard<'a, RawSpinlock, T>;</code></pre>
<p> </p>
<p><strong>mio</strong></p>
<p>看起来有点像minio,但和minio不同,mio用于构建非阻塞的IO应用,而且它的实现比较快,层级比较低。能尽量减少操作系统的负荷。</p>
<p>mio包括多个子模块,包括event,features,guide,net,windows。</p>
<p> </p>
<p><strong>parking_lot</strong></p>
<p>提供了比rust标准库更小更快更灵活的实现,包括Mutex,RwLock,Condvar,Once等.</p>
<p>看起来有点像lock_api</p>
<p> </p>
<p><strong>parking_lot_core</strong></p>
<p>和parking_lot有关的内容。</p>
<p> </p>
<p><strong>pin_project_lite</strong></p>
<p>轻量版本的pin-project。</p>
<p> </p>
<p><strong>proc_macro2</strong></p>
<p>过程宏的包装器。</p>
<p>例如:</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>extern crate proc_macro;
#
pub fn my_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let output: proc_macro2::TokenStream = {
/* transform input */
};
proc_macro::TokenStream::from(output)
}</code></pre>
<p> </p>
<p><strong>quote</strong></p>
<p>宏,用于把rust语法树数据结构转为源码。</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>let tokens = quote! {
struct SerializeWith #generics #where_clause {
value: &'a #field_ty,
phantom: core::marker::PhantomData<#item_ty>,
}
impl #generics serde::Serialize for SerializeWith #generics #where_clause {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#path(self.value, serializer)
}
}
SerializeWith {
value: #value,
phantom: core::marker::PhantomData::<#item_ty>,
}
};</code></pre>
<p> </p>
<p><strong>scopeguard</strong></p>
<p>范围守卫,允许匿名函数在范围之外运行。</p>
<p>看起来像歪门邪道!</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>extern crate scopeguard;
fn f() {
let _guard = scopeguard::guard((), |_| {
println!("Hello Scope Exit!");
});
// rest of the code here.
// Here, at the end of `_guard`'s scope, the guard's closure is called.
// It is also called if we exit this scope through unwinding instead.
}</code></pre>
<p> </p>
<p><strong>smallvec</strong></p>
<p>顾名思义,是小型的向量,但它的大小可变。可以提升性能,针对只需要很少数据的场景。</p>
<p> </p>
<p><strong>socket2</strong></p>
<p>用于创建和使用套接字,和网络编程相关。</p>
<p>但它的缺陷是不够通用,因为要求尽量使用操作系统的已有的能力。</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>use std::net::{SocketAddr, TcpListener};
use socket2::{Socket, Domain, Type};
// Create a TCP listener bound to two addresses.
let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
socket.set_only_v6(false)?;
let address: SocketAddr = "[::1]:12345".parse().unwrap();
socket.bind(&address.into())?;
socket.listen(128)?;
let listener: TcpListener = socket.into();
// ...</code></pre>
<p> </p>
<p> </p>
<p><strong>syn</strong></p>
<p>名字容易联想到synchronize,其实应该是syntax。</p>
<p>用于解析rust的代码流为语法树,进场和其它宏相关。</p>
<p>例如:</p>
<pre class="highlighter-prismjs language-rust prismjs-lines-highlighted" tabindex="0"><code>use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#
pub fn my_macro(input: TokenStream) -> TokenStream {
// Parse the input tokens into a syntax tree
let input = parse_macro_input!(input as DeriveInput);
// Build the output, possibly using quasi-quotation
let expanded = quote! {
// ...
};
// Hand the output tokens back to the compiler
TokenStream::from(expanded)
}</code></pre>
<p> </p>
<p><strong>tokio</strong></p>
<p>运行时,用于编写可靠的网络应用,同时还不需要牺牲速度。</p>
<p>它是一个事件驱动,非阻塞的平台,能用于编写异步应用。</p>
<p> </p>
<p><strong>其它</strong></p>
<p>tokio_macros,unicode_ident,windows_sys,windows_targets,windows_x86_64_msvc.</p>
<p> </p>
</div>
<div id="MySignature" role="contentinfo">
<p>本文来自博客园,作者:正在战斗中,转载请注明原文链接:https://www.cnblogs.com/lzfhope/p/18891803</p><br><br>
来源:https://www.cnblogs.com/lzfhope/p/18891803
頁:
[1]