Asynchronous Runtime
note
- โดยปกติแล้ว การทำงานของ Rust จะเป็น Synchronous หมายความว่า ทุกอย่างจะต้องทำงานอย่างต่อเนื่องจากการทำงานของตัวมันเอง
- แต่ถ้าหากต้องการทำงานแบบ Parallel หรือ Asynchronous, Rust เองก็มีการสนับสนุนในการทำงานแบบนั้นได้ โดยการใช้งาน Thread หรือ Task หรือ Future (คล้ายๆ Promise ใน NodeJS) ด้วยเช่นกัน
- แต่ก็มีทางเลือกอีกทางที่ง่ายกว่านั้น ก็คือการใช้งาน Async Runtime ผ่าน Crate ที่ชื่อว่า
Tokio Tokioคือ Crate ที่สร้างขึ้นมาเพื่อสนับสนุนการทำงานแบบ Asynchronous และ Parallel ใน Rust โดยทำหน้าที่จัดการการทำงานของ Thread หรือ Task หรือ Future ให้ง่ายขึ้น ผ่าน SyntaxAsync/Awaitที่เราคุ้นเคยกันมาจาก NodeJS- Web framework หรือ Database ส่วนใหญ่ของ Rust จะทำงานแบบ Asynchronous อยู่บน
Tokio Runtimeเช่นAxum,Actix,Rocket,Warp
tip
Async/AwaitของTokio Rustจะใช้งานคล้ายกับAsync/Awaitของ NodeJS แต่เบื้องหลังการทำงานจะแตกต่างกัน- สำหรับ NodeJS จะใช้งานผ่าน
Event Loopที่ทำงานอยู่ในภายใน Single Thread - สำหรับ Rust จะใช้งานผ่าน
ThreadหรือTaskที่ทำงานอยู่ในภายในThread Poolซึ่งจะทำงานอยู่บน Core ของ CPU ที่มีทั้งหมด ทำให้ Rust สามารถทำงานแบบ Parallel ได้โดยสมบูรณ์
- สำหรับ NodeJS จะใช้งานผ่าน
How Tokio Runtime Work
tip
- Thread Pool:
Tokioใช้ thread pool ในการจัดการ task ต่างๆ โดยจะมีการสร้าง thread ขึ้นมาเป็นจำนวนหนึ่งตามจำนวน core ของ CPU ที่มีอยู่ - Task Scheduling:
Tokioมี task scheduler ที่ทำหน้าที่จัดสรร task ต่างๆ ให้กับ thread pool เพื่อให้การทำงานเป็นไปอย่างมีประสิทธิภาพ - Async/Await: การใช้งาน
asyncและawaitในTokioจะช่วยให้การเขียนโค้ดแบบ asynchronous ง่ายขึ้น โดยไม่ต้องจัดการกับ thread โดยตรง - Event Loop:
Tokioมี event loop ที่ทำหน้าที่ตรวจสอบ event ต่างๆ และจัดการกับ task ที่พร้อมจะทำงาน - Resource Management:
Tokioจัดการ resource ต่างๆ เช่น network connection, file I/O, และ timer ให้เป็นไปอย่างมีประสิทธิภาพ - Thread Safety:
Tokioจัดการ thread ให้เป็นไปอย่างปลอดภัยในการเข้าถึง resource ต่างๆ โดยใช้SendและSynctrait ของ Rust เพื่อให้แน่ใจว่า data ที่ถูกแชร์ระหว่าง thread นั้นปลอดภัย
How to use Tokio Runtime
Add Dependency
[dependencies]
tokio = { version = "1.0", features = ["macro", "rt-multi-thread"] }
or
cargo add tokio -F rt-multi-thread,macro
Change Main Function to #[tokio::main]
#[tokio::main] async fn main() { println!("Hello, world!"); }
Use Async/Await Function
tip
async fnคือ Function ที่ทำงานแบบ Asynchronous (ไม่ block การทำงานของ Thread)async fnจะต้องถูกเรียกใช้ผ่าน.awaitเสมอ- code ที่อยู่บรรทัดถัดจาก
.awaitจะต้องรอการทำงานของasync fnให้เสร็จสิ้นก่อนจึงจะทำงานต่อได้ - เหมาะกับงานประเภท
I/O-boundที่มักจะใช้เวลาส่วนใหญ่ในการรอการตอบสนองจากระบบภายนอก (เช่น การอ่าน/เขียนไฟล์, การเชื่อมต่อเครือข่าย)async fnช่วยให้สามารถปล่อยทรัพยากร (เช่น thread) ให้ทำงานอื่นได้ในระหว่างที่รอ I/O - จะต้องประกาศ
fn mainเป็นasync fnด้วย#[tokio::main]ก่อนเท่านั้น
// จำเป็นต้องมีการประกาศ `#[tokio::main]` เพื่อทำให้ application นี้รองรับการทำงานแบบ Asynchronous #[tokio::main] async fn main() { // required `.await` to run async function do_something().await; } // Define `async` keyword in front of function async fn do_something() { // TODO: Implement }
Use Spawn Task to Run Background Process
tip
tokio::spawnจะสร้าง task ใหม่ที่ทำงานอยู่เบื้องหลังของโปรแกรม และจะเริ่มทำงานทันที่ถูกสร้าง tasktokio::spawnจะแตกต่างจากasync fnตรงที่tokio::spawnไม่จำเป็นต้องรอการทำงานของ task ให้เสร็จสิ้นก่อนที่จะทำงาน code ในบรรทัดถัดไปได้- เหมาะกับงานประเภท
CPU-boundที่สามารถทำงานพร้อมกันหลาย process ได้ - สามารถรอ task ให้ทำงานเสร็จสิ้นก่อนที่จะทำงานต่อได้ผ่าน
.awaitได้เช่นกัน - จะต้องประกาศ
fn mainเป็นasync fnด้วย#[tokio::main]ก่อนเท่านั้น
use tokio::time::{sleep, Duration}; #[tokio::main] async fn main() { println!("เริ่มโปรแกรม"); // สร้าง task เพื่อให้ทำงานอยู่เบื้องหลังของโปรแกรม let task = tokio::spawn(async { for i in 1..=5 { sleep(Duration::from_secs(1)).await; println!("Task ทำงาน: ครั้งที่ {}", i); } println!("Task เสร็จสมบูรณ์!"); }); // รอสักครู่เพื่อให้เห็นผลของ task println!("รอ task ทำงาน"); sleep(Duration::from_secs(3)).await; println!("หมดเวลา"); // รอ task ให้ทำงานเสร็จสิ้น // task.await.unwrap(); println!("โปรแกรมหลักจบการทำงาน"); }