Rust 語言學習筆記(四) I O

2021-09-20 06:08:10 字數 3015 閱讀 6675

寫在前面:這是一篇近一年前的草稿了,翻出來發現,關於task(已改名為thread)退出的一些做法仍然適用,而且 zmq.rs 0.2 不出意外也要用到,所以仍然把這篇寫完貼出來備查。但請注意,文中關於libgreen的一些描述已不屬實。

這一篇隔的時間比較長,期間我們的遊戲在準備上線,所以也沒時間寫 rust,著重在課餘時間研讀了各種文件、原始碼和 issue report——關於 rust 的 i/o。

我的 zmq.rs 專案終於開始碰到網路操作了。由於 rust 給封裝出來的 i/o 介面都是相對於task同步的,所以目前來看還得給每一批 i/o 操作建立乙個task

這裡得多插一點內容了。首先是關於 zeromq,它的乙個 socket 是可以跟很多個別的 zmq socket 通訊的,而且是可以通過不同的 endpoint 來完成。比如乙個 rep 服務端 socket,同時監聽了 8080 和 9090 兩個埠(endpoint),每個埠可能都有數十個 req 客戶端 socket 連線上去;更有甚者,這個 rep socket 也可以主動去連線某些客戶端 socket 監聽的 endpoint,上門服務。而在所有這些網路拓撲結構的上面,乙個 rep socket 對程式設計師的介面是始終一致的,您只需要反覆地從這個 rep socket 讀乙個資料報,然後發乙個資料報就好了。

這樣一來呢,我就得給 rep socket 底層的每乙個 tcp socket 連線建立至少乙個task,以滿足其併發性。之前我們有提到,rust 提供了libgreenlibnative兩種執行時環境,對應了兩種不同的task實現模型。對於 zmq.rs 來說,基於目前的阻塞式的 i/o 介面來看,我們很有可能需要建立大量的task——這對於libgreenm:n模型來說是輕而易舉的,但對於libnative來說卻是值得商榷的,因為1:1的模型意味著我們將會用大量作業系統的執行緒來微操極少量的 zmq socket,這對於追求高併發的 zmq 也許並不是乙個好主意——雖然 rust 的task模型能極大地避免常規多執行緒程式設計中最糟心的那一部分,但是記憶體占用會不會太高(哈!高!-2015),上下文切換的代價會不會太大等問題還有待於進一步測試。如果結果不理想,也許每批 i/o 乙個task的這種設計就要被推倒,新的設計將需要 rust 提供非同步的 i/o 介面。(0.2 確實將這麼改 -2015

回到原來的話題。因為建立了好多task,一定會碰到的問題就是怎麼結束它們,所以我一上來就打算先看一眼這個問題。沒想到這一看,看出了好多問題。

結果逐步明朗,原來 rust 在 0.9 之前確實有過taskkill功能,是通過 supervisor 模型實現的——即一對task,任何乙個掛掉都會導致另乙個掛掉。但是呢,由於一些原因這個功能被砍掉了,也就是說,在 rust 裡,乙個task只能從內部拋錯誤死掉,沒有辦法從外部直接殺掉。另外,這個還(居然!)導致了我的第乙個 stackoverflow 回答。

既然無法從別的task中主動殺掉乙個task,那麼就想辦法讓這個task自殺。乙個長時間執行的task通常處於兩種狀態:1、執行**;2、等待事件。

對於乙個正在執行**的task,我們是無法讓task自己忽然想到該結束了然後戛然而止。只有在某些特殊情況下,我們才能手工寫一些**,讓程式執行一段**之後,去檢查一下是否應該結束了,比如在乙個死迴圈裡:

rustwhile self.running
而大多數其他情況下,從事件等待中跳出來結束乙個task更為常見。這裡也分兩種情況:a、等待 i/o 事件;b、等待channel事件。兩種情況處理都比較簡單,a 的話就給呼叫加乙個稍短的超時,然後重複前面的那個例子,比如:

rusttcp_stream.set_read_timeout(some(1000));

while self.running

而對於等待乙個channeltask來說就更容易了,只要channel的另一端銷毀了,這個等待的呼叫就會自動結束,只需要正確處理呼叫結果就好了。

其實,上面兩個例子中的self.running應(至少)為乙個arc,因為需要從別的task中來設定這個值。這裡還有一種也是用channel的處理方式,就是在每次迴圈的開始處,向乙個連線到父taskchannelsender端,傳送乙個空白訊息,這樣如果另一端已經銷毀了,傳送會失敗,也就意味著我們該退出這個task了,比如這樣:

rustlet (tx, rx) = channel();

// ... in task

let mut a = tcplistener::bind("127.0.0.1:8482").listen().unwrap();

a.set_timeout(some(1000));

loop

err(ref e) if e.kind == timedout =>

err(e) => println!("err: {}", e), // something else

}}

Rust語言學習筆記(3)

泛型列舉 generic enums enum option let x option some 5 let y option some 5.0f64 enum result 泛型函式 generic functions fn takes anything x t fn takes two of t...

Swift語言學習筆記(四)

67.如果結構體 struct 的例項被宣告為常量的話,就不能對其屬性進行修改,即使是 var型別的屬性。而對於類 class 來說則不是這樣,如果乙個類的例項被宣告為常量,仍然可以修改其 var屬性。68.惰性屬性 lazy property 使用關鍵字 lazy 新增在宣告前面 惰性屬性在使用時...

c語言學習筆記四

結構體 復合型別和結構體 復合型別 示例 struct test sturuct 如果用這種復合型別來定義變數 示例 struct test stuructz1,z2 定義訪問結構體 include int main void z int x 3 z.x x z.y 4 printf z f f z...