譯者 | 彎月
譯者 | 彎月 責(zé)編 | 歐陽(yáng)姝黎
出品 | CSDN(ID:CSDNnews)
Rust的異步功能很強(qiáng)大,但也以晦澀難懂著稱。在本文中,我將總結(jié)之前提過(guò)的一些想法,并給出一些新的點(diǎn)子,看看這些想法放在一起能產(chǎn)生什么效果。
本文只是一個(gè)思想實(shí)驗(yàn)。對(duì)Rust進(jìn)行大改造很麻煩,因此我們需要一個(gè)精確的方法來(lái)找出優(yōu)缺點(diǎn),并確定某個(gè)改動(dòng)是否值得。我知道一些觀點(diǎn)會(huì)產(chǎn)生完全相反的看法,所以我建議你用一種開(kāi)放的心態(tài)閱讀本文。
在對(duì)Rust中實(shí)現(xiàn)異步的不同方式進(jìn)行探索之前,我們應(yīng)該首先了解何時(shí)應(yīng)該使用異步編程。畢竟,異步編程并不像僅僅使用線程那么容易。那么異步的好處是什么?有人會(huì)說(shuō)是性能原因,異步代碼更快,因?yàn)榫€程的開(kāi)銷太大了。實(shí)際情況更復(fù)雜。根據(jù)具體情況不同,在以I/O為主的應(yīng)用程序中使用線程有可能更快。例如,一個(gè)基于線程的echo服務(wù)器在并發(fā)數(shù)小于100的時(shí)候比異步更快。但在并發(fā)數(shù)超過(guò)100之后,線程的性能就會(huì)下降,但也不是急劇下降。
我認(rèn)為,使用異步的更好的理由是可以更有效地針對(duì)復(fù)雜的流程控制進(jìn)行建模。例如,如果不適用異步編程,那么暫停或取消一個(gè)正在進(jìn)行的操作就會(huì)非常困難。而且,使用線程時(shí),在各個(gè)連接之間進(jìn)行協(xié)調(diào)需要使用同步原語(yǔ),這就會(huì)導(dǎo)致競(jìng)爭(zhēng)。使用異步編程,可以在同一個(gè)線程中對(duì)多個(gè)連接進(jìn)行操作,從而避免了同步原語(yǔ)。
Rust的異步模型能夠非常好地對(duì)復(fù)雜流程控制進(jìn)行建模。例如,mini-redis的subscribe命令(
https://github.com/tokio-rs/mini-redis/blob/master/src/cmd/subscribe.rs#L94-L156)就非常精練、非常優(yōu)雅。但異步也不是萬(wàn)能靈藥。許多人都認(rèn)為異步Rust的學(xué)習(xí)曲線非常復(fù)雜。盡管入門(mén)很容易,但很快就會(huì)遇到陡峭的曲線。很多人付出了很多努力,盡管有幾個(gè)方面有待改進(jìn),但我相信,異步Rust最大的問(wèn)題就在于會(huì)違反“最小驚訝原則”。
舉個(gè)例子。同學(xué)A在學(xué)習(xí)Rust時(shí)閱讀了Rust的教科書(shū)和Tokio的指南,打算寫(xiě)一個(gè)聊天服務(wù)器作為練習(xí)。他選了一個(gè)基于行的簡(jiǎn)單協(xié)議,將每一行編碼,添加前綴表示行的長(zhǎng)度。解析行的函數(shù)如下:
let len = socket.read_u32.await?;
let mut line = vec![0; len];
socket.read_exact(&mut line).await?;
let line = str::from_utf8(line)?;
Ok(line)
}
這段代碼除了async和await關(guān)鍵字之外,跟阻塞的Rust代碼沒(méi)有什么兩樣。盡管同學(xué)A從來(lái)沒(méi)有寫(xiě)過(guò)Rust,但閱讀并理解這個(gè)函數(shù)完全沒(méi)問(wèn)題,至少?gòu)乃约旱慕嵌瓤慈绱恕T诒镜販y(cè)試時(shí),聊天服務(wù)器似乎也能正常工作,于是他給同學(xué)B發(fā)送了一個(gè)鏈接。但很不幸,在進(jìn)行了一些聊天后,服務(wù)器崩潰了,并返回了“invalid UTF-8”的錯(cuò)誤。同學(xué)A很迷惑,他檢查了代碼,但并沒(méi)有發(fā)現(xiàn)什么錯(cuò)誤。
那么問(wèn)題在哪兒?似乎該任務(wù)在調(diào)用棧的更高層的位置使用了一個(gè)select!:
loop {
select! {
line_in = parse_line(&socket) => {
if let Some(line_in) = line_in {
broadcast_line(line_in);
} else {
// connection closed, exit loop
break;
}
}
line_out = channel.recv => {
write_line(&socket, line_out).await;
}
}
}
假設(shè)channel上收到了一條消息,而此時(shí)parse_line在等待更多數(shù)據(jù),那么select!就會(huì)放棄parse_line操作,從而導(dǎo)致丟失解析中的狀態(tài)。在后面的循環(huán)迭代中,parse_line再次被調(diào)用,從一幀的中間開(kāi)始,從而導(dǎo)致讀入了錯(cuò)誤數(shù)據(jù)。
問(wèn)題在此:任何Rust異步函數(shù)都可能被調(diào)用者隨時(shí)取消,而且與阻塞Rust不同,這里的取消是一個(gè)常見(jiàn)的異步操作。更糟糕的是,沒(méi)有任何新手教程提到了這一點(diǎn)。
Future
如果能改變這一點(diǎn),讓異步Rust每一步的行為符合初學(xué)者預(yù)期呢?如果行為必須根據(jù)預(yù)期得到,那么必然有一個(gè)能接受的點(diǎn),為初學(xué)者指引正確的方向。此外,我們還希望最大程度地減少學(xué)習(xí)過(guò)程中的意料之外,特別是剛開(kāi)始的時(shí)候。
我們先來(lái)改變意料之外的取消問(wèn)題,即讓異步函數(shù)總是能夠完成執(zhí)行。當(dāng)future能夠保證完成后,同學(xué)A發(fā)現(xiàn)異步Rust的行為跟阻塞Rust完全相同,只不過(guò)是多了兩個(gè)關(guān)鍵字async和await而已。生成新任務(wù)會(huì)增加并發(fā),也會(huì)增加任務(wù)之間的協(xié)調(diào)通道數(shù)量。select!不再能夠接受任意異步語(yǔ)句,而只能與通道或類似通道的類型(例如JoinHandle)一起使用。
使用能保證完成的future后,同學(xué)A的聊天服務(wù)器如下:
async fn handle_connection(socket: TcpStream, channel: Channel) {
let reader = Arc::new(socket);
let writer = reader.clone;
let read_task = task::spawn(async move {
while let Some(line_in) in parse_line(&reader).await {
broadcast_line(line_in);
}
});
loop {
// `channel` and JoinHandle are both "channel-like" types.
select! {
res = read_task.join => {
// The connection closed, exit loop
break;
}
line_out = channel.recv => {
write_line(&writer, line_out).await;
}
}
}
}
這段代碼與前面的示例很相似,但由于所有異步語(yǔ)句必然會(huì)完成,而且select!只接受類似于通道的類型,因此parse_line的調(diào)用被移動(dòng)到了一個(gè)生成的任務(wù)中。select要求類似于通道的類型,這能夠保證放棄丟失的分支是安全的。通道可以存儲(chǔ)值,而且接收值是原子操作。丟失select的分支并不會(huì)導(dǎo)致取消時(shí)丟失數(shù)據(jù)。
取消
如果寫(xiě)入時(shí)發(fā)生錯(cuò)誤會(huì)怎樣?現(xiàn)狀下read_task會(huì)繼續(xù)執(zhí)行。然而,同學(xué)A希望它能出錯(cuò),并優(yōu)雅地關(guān)閉連接和所有任務(wù)。不幸的是,這里就會(huì)遇到設(shè)計(jì)上的難題。如果我們能夠隨時(shí)放棄任何異步語(yǔ)句,那么取消就非常容易了,只需要放棄future就可以。我們需要一種方法來(lái)取消正在執(zhí)行的操作,因?yàn)檫@是使用異步編程的主要目的之一。為了實(shí)現(xiàn)這一點(diǎn),JoinHandle提供了cancel方法:
async fn handle_connection(socket: TcpStream, channel: Channel) {
let reader = Arc::new(socket);
let writer = reader.clone;
let read_task = task::spawn(async move {
while let Some(line_in) in parse_line(&reader).await? {
broadcast_line(line_in)?;
}
Ok()
});
loop {
// `channel` and JoinHandle are both "channel-like" types.
select! {
_ = read_task.join => {
// The connection closed or we encountered an error,
// exit the loop
break;
}
line_out = channel.recv => {
if write_line(&writer, line_out).await.is_err {
read_task.cancel;
read_task.join;
}
}
}
}
}
但是cancel能做什么呢?它并不能立即終止任務(wù),因?yàn)楝F(xiàn)在異步語(yǔ)句是保證能夠執(zhí)行完成的。但我們的確需要停止處理并盡快返回。相反,被取消的任務(wù)中的所有資源類型都應(yīng)該停止執(zhí)行,并返回“被中斷”的錯(cuò)誤。進(jìn)一步的嘗試也應(yīng)該返回錯(cuò)誤。這種策略與Kotlin很相似,只不過(guò)Kotlin會(huì)拋出異常而已。如果在任務(wù)取消時(shí),read_task正在parse_line中等待socket.read_u32,那么read_u32函數(shù)會(huì)立即返回Err(
io::ErrorKind::Interrupted)。操作符?會(huì)在任務(wù)中向上冒泡,導(dǎo)致整個(gè)任務(wù)中斷。
乍一看,這種行為非常像其他任務(wù)停止的行為,但其實(shí)不一樣。對(duì)于同學(xué)A而言,當(dāng)前的異步Rust的終止行為看起來(lái)就像任務(wù)不確定地發(fā)生掛起一樣。如果能強(qiáng)制資源(例如套接字)在取消時(shí)返回錯(cuò)誤,就能跟蹤取消的流程。同學(xué)A可以添加println!語(yǔ)句或使用其他調(diào)試策略來(lái)調(diào)查什么導(dǎo)致了任務(wù)中斷。
AsyncDrop
然而,同學(xué)A并不知道,他的聊天服務(wù)器使用了io-uring來(lái)避免了絕大部分系統(tǒng)調(diào)用。由于future能保證完成,再加上AsyncDrop,就可以透明底使用io-uring API。當(dāng)同學(xué)A在handle_connection的末尾drop TcpStream時(shí),套接字會(huì)異步地關(guān)閉。為了實(shí)現(xiàn)這一點(diǎn),TcpStream的AsyncDrop實(shí)現(xiàn)如下:
impl AsyncDrop for TcpStream {
async fn drop(&mut self) {
self.uring.close(self.fd).await;
}
}
有人提出了一個(gè)絕妙的方法在traits中使用async(
https://hackmd.io/bKfiVPRpTvyX8JK_Ng2EWA?view)。唯一的問(wèn)題就是如何處理隱含的.await點(diǎn)。目前,異步地等待一個(gè)future需要進(jìn)行一次.await調(diào)用。而當(dāng)一個(gè)值離開(kāi)async上下文的范圍時(shí),編譯器會(huì)為AsyncDrop trait添加一個(gè)隱藏的yield點(diǎn)。這個(gè)行為違反了最少意料之外原則。那么,既然其他的點(diǎn)都是明示的,為何此處需要一個(gè)隱含的await點(diǎn)?
解決“有時(shí)需要隱含drop”的問(wèn)題的提議之一就是,要求使用明示的函數(shù)調(diào)用執(zhí)行異步的drop:
my_tcp_stream.read(&mut buf).await?;
async_drop(my_tcp_stream).await;
當(dāng)然,如果用戶忘記調(diào)用async drop怎么辦?畢竟,編譯器在阻塞Rust中會(huì)自動(dòng)處理drop,而且這是個(gè)非常強(qiáng)大的功能。而且,注意上述代碼有一個(gè)小問(wèn)題:?操作符在讀取錯(cuò)誤時(shí)會(huì)跳過(guò)async_drop調(diào)用。Rust編譯器能對(duì)此問(wèn)題給出警告,但怎么修復(fù)呢?有辦法讓?與明示的async_drop兼容嗎?
去掉.await
如果不要求明示的async drop調(diào)用,而是去掉await關(guān)鍵字怎么樣?同學(xué)A就不需要在調(diào)用異步函數(shù)(如socket.read_u32().await)之后使用.await了。在異步上下文中調(diào)用異步函數(shù)時(shí),.await就變成了隱含的。
似乎這是如今Rust的一大進(jìn)步。但我們依然可以對(duì)這個(gè)假設(shè)提出質(zhì)疑。隱含的.await只能在異步語(yǔ)句中發(fā)生,因此它的應(yīng)用比較有限,而且依賴于上下文。同學(xué)A只有通過(guò)查看函數(shù)定義,才能知道自己位于某個(gè)異步上下文中。此外,如果IDE能高亮顯示某個(gè)yield點(diǎn),就會(huì)非常方便。
去掉.await還有另一個(gè)好處:它能讓異步Rust與阻塞Rust一直。阻塞的概念已經(jīng)是隱含的了。在阻塞Rust中,我們并不會(huì)寫(xiě)my_socket.read(buffer).block?。當(dāng)同學(xué)A編寫(xiě)異步聊天服務(wù)器時(shí),他注意到的唯一區(qū)別就是必須用async關(guān)鍵字來(lái)標(biāo)記函數(shù)。同學(xué)A可以憑直覺(jué)想象異步代碼的執(zhí)行。“懶future”的問(wèn)題不再出現(xiàn),而同學(xué)A也不能無(wú)意間做下面的事,并對(duì)先輸出“two”的情況感到困惑。
async fn my_fn_one {
println!("one");
}
async fn my_fn_two {
println!("two");
}
async fn mixup {
let one = my_fn_one;
let two = my_fn_two;
join!(two, one);
}
.await的RFC中的確有一些對(duì)于隱含await的討論。當(dāng)時(shí),反對(duì)隱含await的最有力的觀點(diǎn)是,await調(diào)用正好標(biāo)記了async語(yǔ)句可以被中止的點(diǎn)。如果采用保證完成的future,這個(gè)觀點(diǎn)就不那么有力了。當(dāng)然,對(duì)于可以安全中止的異步語(yǔ)句,我們還應(yīng)該保留await關(guān)鍵字嗎?這個(gè)問(wèn)題需要一個(gè)答案。但無(wú)論如何,去掉“.await”是一個(gè)非常大的挑戰(zhàn),必須謹(jǐn)慎行事。需要進(jìn)行易用性研究,表明其優(yōu)點(diǎn)大于缺點(diǎn)才行。
帶有作用域的任務(wù)
到目前為止,同學(xué)A已經(jīng)可以使用異步Rust構(gòu)建聊天服務(wù)器,而且不需要學(xué)習(xí)太多新概念,也不會(huì)遇到無(wú)法預(yù)測(cè)的行為。他了解了select!,但編譯器會(huì)強(qiáng)制在類似于通道的類型中進(jìn)行選擇。除此之外,同學(xué)A還給函數(shù)添加了async,而且運(yùn)行良好。他把代碼展示給同學(xué)B看,并詢問(wèn)是否需要將套接字放在一個(gè)Arc中。同學(xué)B建議他閱讀一下帶有作用域的任務(wù)(scoped tasks),借此避免分配。
帶有作用域的任務(wù)等價(jià)于crossbeam的“帶有作用域的線程”,只不過(guò)是異步的。這個(gè)任務(wù)可以通過(guò)生成者借用數(shù)據(jù)。同學(xué)A可以使用帶有作用域的任務(wù)來(lái)避免在連接處理函數(shù)中使用Arc:
async fn handle_connection(socket: TcpStream, channel: Channel) {
task::scope(async |scope| {
let read_task = scope.spawn(async || {
while let Some(line_in) in parse_line(&socket)? {
broadcast_line(line_in)?;
}
Ok()
});
loop {
// `channel` and JoinHandle are both "channel-like" types.
select! {
_ = read_task.join => {
// The connection closed or we encountered an error,
// exit the loop
break;
}
line_out = channel.recv => {
if write_line(&writer, line_out).is_err {
break;
}
}
}
}
});
}
保證安全的關(guān)鍵是要保證,作用域的生存周期要大于在該作用域范圍內(nèi)生成的所有任務(wù),換句話說(shuō),確保異步語(yǔ)句能夠完成。但有一個(gè)缺點(diǎn)。啟用帶有作用域的任務(wù)會(huì)使“Future::poll”變得不安全,因?yàn)楸仨殞?duì)future的完成情況進(jìn)行輪詢,以保證內(nèi)存安全性。這種不安全性會(huì)導(dǎo)致Future的實(shí)現(xiàn)更難。為了降低難度,我們需要盡可能避免用戶自己實(shí)現(xiàn)Future,包括實(shí)現(xiàn)類似于AsyncRead、AsyncIterator等traits。我相信這是一個(gè)可以達(dá)到的目標(biāo)。
除了帶有作用域的任務(wù)之外,保證異步語(yǔ)句的完成,還可以在使用io-uring或與C++ future集成時(shí),讓指針能正確地從任務(wù)傳遞到內(nèi)核。某些情況下,還可能在生成子任務(wù)時(shí)避免分配,對(duì)于某些嵌入式環(huán)境非常有用,盡管這種情況需要一個(gè)略微不同的API。
通過(guò)生成的方式增加并發(fā)
利用今天的異步Rust,應(yīng)用程序可以通過(guò)利用select!或FutureUnordered生成新任務(wù)的方式增加并發(fā)。到目前為止,我們討論了任務(wù)生成和select!。我建議去掉FuturesUnordered,因?yàn)樗?jīng)常會(huì)導(dǎo)致bug。在使用FutureUnordered時(shí),很容易認(rèn)為生成的任務(wù)會(huì)在后臺(tái)執(zhí)行,然后出乎意料地發(fā)現(xiàn)這些任務(wù)不會(huì)有任何進(jìn)展。
相反,我們可以利用帶有作用域的任務(wù)實(shí)現(xiàn)類似的方案:
let greeting = "Hello".to_string;
task::scope(async |scope| {
let mut task_set = scope.task_set;
for i in 0..10 {
task_set.spawn(async {
println!("{} from task {}", greeting, i);
i
});
}
async for res in task_set {
println!("task completed {:?}", res);
}
});
每個(gè)生成的任務(wù)都會(huì)并發(fā)執(zhí)行,從生成者那里借用數(shù)據(jù),而TaskSet能提供一個(gè)類似于FuturesUnordered,但不會(huì)導(dǎo)致災(zāi)難的API。至于緩存流等其他原語(yǔ)也可以在帶有作用域的任務(wù)上實(shí)現(xiàn)。
還可以在這些原語(yǔ)之上實(shí)現(xiàn)一些新的并發(fā)原語(yǔ)。例如,可以實(shí)現(xiàn)類似于Kotlin的結(jié)構(gòu)化并發(fā)。之前有人曾討論過(guò)這個(gè)問(wèn)題(
https://github.com/tokio-rs/tokio/issues/1879),但異步Rust的當(dāng)前模型無(wú)法實(shí)現(xiàn)這一點(diǎn)。而將異步Rust改為保證完成,就能解鎖這一領(lǐng)域。
select!怎么辦?
本文開(kāi)頭我說(shuō)過(guò),使用異步編程可以更有效地對(duì)復(fù)雜的流程控制進(jìn)行建模。目前最有效的原語(yǔ)為select!。我還提議,將select!改為只接受類似于通道的類型,這樣可以強(qiáng)制同學(xué)A為每個(gè)連接生成兩個(gè)任務(wù),實(shí)現(xiàn)讀寫(xiě)的并發(fā)性。生成任務(wù)能防止在取消讀操作的時(shí)候出現(xiàn)bug,還能重寫(xiě)讀操作,以處理意料之外的取消。例如,mini-redis在解析幀的時(shí)候,我們首先將接收到的數(shù)據(jù)保存到緩沖區(qū)中。當(dāng)讀操作被取消時(shí),位于緩沖區(qū)中的數(shù)據(jù)不會(huì)丟失。下次調(diào)用讀操作會(huì)從中斷的地方繼續(xù)。因此Mini-redis的讀操作對(duì)于中止是安全的(abort-safe)。
如果不將select!限制在類似于通道的類型上,而是將其限制在對(duì)于中止是安全的操作上,會(huì)怎樣?從通道中接收數(shù)據(jù)是中止安全的,但從帶有緩沖區(qū)的I/O處理函數(shù)中讀取也是中止安全的。這里的關(guān)鍵是,不應(yīng)該假設(shè)所有異步操作都是中止安全的,而是應(yīng)該要求開(kāi)發(fā)者向函數(shù)定義中添加#[abort_safe](或async(abort))。這種策略有幾個(gè)好處。首先,當(dāng)同學(xué)A學(xué)習(xí)異步Rust時(shí),它不需要知道任何有關(guān)安全性的概念。即使不理解這個(gè)概念,僅通過(guò)生成任務(wù)來(lái)獲得并發(fā)性,也可以實(shí)現(xiàn)一切:
#[abort_safe]
async fn read_line(&mut self) -> io::Result<Option<String>> {
loop {
// Consume a full line from the buffer
if let Some(line) = self.parse_line? {
return Ok(line);
}
// Not enough data has been buffered to parse a full line
if 0 == self.socket.read_buf(&mut self.buffer)? {
// The remote closed the connection.
if self.buffer.is_empty {
return Ok(None);
} else {
return Err("connection reset by peer".into);
}
}
}
}
不再默認(rèn)要求中止安全語(yǔ)句,而是由開(kāi)發(fā)者自行標(biāo)注。這種自行標(biāo)注的策略符合撤銷安全性的模式。當(dāng)新的開(kāi)發(fā)者閱讀代碼時(shí),這個(gè)標(biāo)注會(huì)告訴他們?cè)摵瘮?shù)必須保證中止安全。rust編譯器甚至可以對(duì)于標(biāo)注了#[abort_safe]的函數(shù)提供額外的檢查和警告。
現(xiàn)在同學(xué)A可以在select!的循環(huán)中使用read_line了:
loop {
select! {
line_in = connection.read_line? => {
if let Some(line_in) = line_in {
broadcast_line(line_in);
} else {
// connection closed, exit loop
break;
}
}
line_out = channel.recv => {
connection.write_line(line_out)?;
}
}
}
混合使用中止安全和非中止安全
#[abort_safe]注釋引入了兩個(gè)異步語(yǔ)句的變種。混合使用中止安全和非中止安全需要特別考慮。不論從中止安全還是從非中止安全的上下文中,都可以調(diào)用一個(gè)中止安全的函數(shù)。然而,Rust編譯器會(huì)阻止從中止安全的上下文中調(diào)用非中止安全的函數(shù),并提供一個(gè)有幫助的錯(cuò)誤信息:
async fn must_complete { ... }
#[abort_safe]
async fn can_abort {
// Invalid call => compiler error
must_complete;
}
async fn must_complete { ... }
#[abort_safe]
async fn can_abort {
// Valid call
spawn(async { must_complete }).join;
}
開(kāi)發(fā)者可以通過(guò)生成新任務(wù)的方式,從非中止安全函數(shù)中獲得中止安全的上下文。
異步語(yǔ)句的兩個(gè)新變種會(huì)增加語(yǔ)言的復(fù)雜性,但這個(gè)復(fù)雜性僅在學(xué)習(xí)曲線的后期才出現(xiàn)。在剛開(kāi)始學(xué)習(xí)Rust時(shí),默認(rèn)的異步語(yǔ)句是非中止安全的。從這個(gè)上下文中,學(xué)習(xí)者可以不用關(guān)心中止安全性而直接調(diào)用異步函數(shù)。中止安全會(huì)在異步Rust的教程的后期作為一個(gè)可選的話題出現(xiàn)。
漫漫長(zhǎng)路
從目前的默認(rèn)要求中止安全的異步模型改變成保證完成的模型,需要一個(gè)全新的Rust版本。處于討論的目的,我們假設(shè)Rust 2026版引入了該變動(dòng)。那么該版本中的Future trait將改變?yōu)楸WC完成的future,因此無(wú)法與老版本的trait兼容。相反,2026版中的舊trait將改名為AbortSafeFuture(名稱暫定)。
在2026版中,給異步語(yǔ)句添加#[abort_safe]會(huì)生成一個(gè)AbortSafeFuture實(shí)現(xiàn),而不是Future。2026之前版本中編寫(xiě)的任何異步函數(shù)都實(shí)現(xiàn)了AbortSafeFuture trait,因此任何已有的異步代碼都能與新版本兼容(別忘了,中止安全的函數(shù)可以從任何上下文中調(diào)用)。
一些想法
我討論了Rust可能出現(xiàn)的一些改動(dòng)。簡(jiǎn)單地總結(jié)一下:
-
異步函數(shù)保證完成
-
去掉await關(guān)鍵字
-
引入#[abort_safe]標(biāo)注,表示異步函數(shù)是中止安全的
-
限制select!,僅接受中止安全的分支
-
取消已生成的任務(wù)的方式是禁止資源完成
-
支持帶有作用域的任務(wù)
我相信,這些改動(dòng)可以極大地簡(jiǎn)化Rust異步,盡管進(jìn)行這些改動(dòng)會(huì)影響現(xiàn)狀。在進(jìn)行決定之前,我們還需要更多數(shù)據(jù)。如今的異步代碼有多少是中止安全的?我們能否進(jìn)行易用性研究,以評(píng)價(jià)這些改動(dòng)的好處?Rust擁有兩種風(fēng)格的異步語(yǔ)句,會(huì)帶來(lái)多少認(rèn)知上的困難?
我也希望本文能拋磚引玉,期待其他人能提出別的觀點(diǎn)。現(xiàn)在Rust需要許多觀點(diǎn)來(lái)決定其未來(lái)。
原文鏈接:
https://carllerche.com/2021/06/17/six-ways-to-make-async-rust-easier/
聲明:本文由CSDN翻譯,轉(zhuǎn)載請(qǐng)注明來(lái)源。