惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
量子位
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
B
Blog
U
Unit 42
C
Check Point Blog
I
InfoQ
aimingoo的专栏
aimingoo的专栏
雷峰网
雷峰网
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 【当耐特】
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
H
Help Net Security
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
云风的 BLOG
云风的 BLOG
宝玉的分享
宝玉的分享
爱范儿
爱范儿

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog
谈谈 tokio::select! 的公平性 | Yesterday17's Blog
Yesterday17 · 2024-10-11 · via Yesterday17's Blog

ToC

  • 前言
  • 公平性(Fairness
  • biased 是什么?
  • 参考

前言

Pingora 的源码阅读中,我不止一次看到有这样的用法:

let res = tokio::select! {

biased; // biased select is cheaper and we don't want to drop already buffered requests

res = downstream_session.read_request() => { res }

_ = self.shutdown.notified() => {

// service shutting down, dropping the connection to stop more req from coming in

return None;

}

};

于是正好借着这个机会,来看看 tokio::select! 的一些知识(常识)。

公平性(Fairness

我们知道,Rust 的异步运行时是需要通过 poll 的方式获取每一个 Future 的实际运行状况的。而对于 select! 这种需要「同时」执行多个 FutureBuilder 并同时 poll 的实现,就需要考虑每个分支的执行顺序问题了。

我们以 JavaScript 举个不严谨的例子。假设有以下两个 async function

async function first() {

return 1;

}

function second() {

return new Promise(resolve => setTimeout(resolve, 1e3));

}

第一个 first 是立即返回的实质同步函数,对应了 Rustpoll 直接返回了 Poll::Ready 的情况;而第二个 second 则是等待一秒才会返回。如果我们这样实现 select

function select(...futures) {

while (true) {

for (const future of futures) {

const result = future();

if (result.is_resolved()) {

return result.value;

}

}

}

}

select(first, second);

由于 first 的实现每次都是立即返回,所以这个 select 的实现永远都不会返回 second 的结果。

例子中我们给出的 first 实现是立即返回,但对于实际的实现而言,这个条件可能更宽松。只要排在 select 前面的函数较快返回的概率比排在后面的函数更大,后面的函数返回结果的几率就会显著小于排在前面的函数,造成一定程度的不公平性。而如果是像我们例子里这种立即返回,那么排在后面的函数就永远没有出头之日,造成某种程度的「饿死」。

因此,tokio::select! 的实现对于位于不同「顺位」的 Future 采取了随机选择的方式,以尽量减少这类情况的出现。

biased 是什么?

随机固然保证了一定程度的公平,但其本身就存在一定的开销。此外,对于某些场景,可能也会希望「顺序执行」的稳定性。biased 就是为了这种情景诞生的。还是回到 Pingora 的这个例子:

let res = tokio::select! {

biased; // biased select is cheaper and we don't want to drop already buffered requests

res = downstream_session.read_request() => { res }

_ = self.shutdown.notified() => {

// service shutting down, dropping the connection to stop more req from coming in

return None;

}

};

如果我们希望在处理 shutdown 之前,把已经解析完的所有 request 都通过 read_request 读取出来,此时再使用随机的策略就不大合适了。在这种情况下,我们可以在 select! 的最开头加上 biased;,这样 tokio::select! 就会保证选择的顺序执行,也就能实现我们想要的效果了。

这个写法怎么那么像 “use strict”;

参考