رویکرد Rust به همزمانی را که مبتنی بر مفهوم “همزمانی بدون ترس” است، درک کنید.
Concurrency توانایی یک برنامه برای اجرای چندین کار به طور همزمان در یک هسته CPU است. وظایف همزمان بدون ترتیب مشخص در زمان همپوشانی اجرا و تکمیل میشوند، برخلاف موازیسازی، که در آن وظایف مختلف یا وظایف فرعی یک کار به طور همزمان روی سختافزار با چندین پردازنده اجرا میشوند.
Rust به دلیل ویژگی های عملکردی و پشتیبانی از همزمانی به شیوه ای ایمن و کارآمد متمایز است. رویکرد Rust به همزمانی مبتنی بر مفهوم “همزمانی بیباک” است که در آن زبان قصد دارد نوشتن کد همزمان امن را از طریق مالکیت و سیستم قرضگیری خود آسان کند که قوانین سختگیری را در زمان کامپایل برای جلوگیری از ردیابی دادهها و تضمین ایمنی حافظه اعمال میکند.
درک همزمانی در Rust
Rust چندین ویژگی اولیه همزمان را برای نوشتن برنامه های همزمان، از جمله رشته ها، ارسال پیام، mutexes، انواع اتمی و async/wait برای برنامه نویسی ناهمزمان ارائه می دهد.
در اینجا مروری بر اصول اولیه همزمانی Rust آورده شده است:
- Threads: Rust ماژول std::thread را در کتابخانه استاندارد خود برای ایجاد و مدیریت رشته ها ارائه می دهد. با تابع thread::spawn می توانید نخ های جدید ایجاد کنید. thread::spawn یک بسته می شود که حاوی کد برای اجرا است. همچنین میتوانید رشتههایی را اجرا کنید که میتوانند به صورت موازی اجرا شوند، و Rust مقدمات همگامسازی را برای هماهنگ کردن اجرای آنها فراهم میکند. بررسی کننده قرض اطمینان می دهد که مراجع منجر به رفتارهای غیرمنتظره نمی شود.
- ارسال پیام: مدل همزمانی Rust از ارسال پیام بین رشته ها پشتیبانی می کند. شما از کانال های پیاده سازی شده از طریق ماژول std::sync::mpsc برای ارسال پیام استفاده خواهید کرد. یک کانال از یک فرستنده (فرستنده) و یک گیرنده (گیرنده) تشکیل شده است. نخ ها می توانند پیام ها را از طریق فرستنده ارسال کرده و از طریق گیرنده دریافت کنند. این یک راه امن و هماهنگ برای برقراری ارتباط بین رشته ها را فراهم می کند.
- Mutexes و Atomic Types: Rust مقدمات همگام سازی را فراهم می کند، از جمله mutexes (std:: sync:: Mutex) و انواع اتمی (std:: sync::atomic)، برای اطمینان از دسترسی به اشتراک گذاری انحصاری داده ها. Mutexeها به چندین رشته اجازه میدهند به طور همزمان به دادهها دسترسی داشته باشند و در عین حال از مسابقه دادهها جلوگیری میکنند. انواع اتمی، عملیات اتمی را بر روی داده های مشترک، مانند افزایش شمارنده، بدون نیاز به قفل صریح ارائه می کنند.
- Async/Await و Futures: دستور ناهمگام/انتظار Rust عملکردی را برای نوشتن کد ناهمزمان ارائه می دهد که می توانید همزمان آن را اجرا کنید. برنامههای ناهمزمان به طور مؤثر با وظایف I/O-Bound سروکار دارند و برنامهها را قادر میسازد تا وظایف دیگر را در حالی که منتظر سایر عملیات I/O هستند انجام دهند. دستور async/wait Rust بر اساس قراردادهای آتی است و میتوانید با کتابخانههای async-std یا tokio زمان اجرا، آنها را تقویت کنید.
رزوه های زنگ سبک وزن هستند و عدم وجود سربار زمان اجرا آنها را برای کاربردهای با کارایی بالا مناسب می کند. اصول اولیه Concurrency Rust به طور یکپارچه با چندین کتابخانه و چارچوب برای نیازهای همزمانی مختلف ادغام می شوند.
نحوه استفاده از نخ های Spawn در Rust
شما از ماژول std::thread برای ایجاد نخ ها استفاده خواهید کرد. تابع std::thread::spawn به شما این امکان را می دهد که یک رشته جدید ایجاد کنید که همزمان با رشته اصلی یا هر رشته دیگر موجود در برنامه شما اجرا شود.
در اینجا نحوه ایجاد یک نخ با تابع std::thread::spawn آمده است:
use std::thread;
fn main() {
// Spawn a new thread
let thread_handle = thread::spawn(|| {
// Code executed in the new thread goes here
println!("Hello from the new thread!");
});
// Wait for the spawned thread to finish
thread_handle.join().unwrap();
// Code executed in the main thread continues here
println!("Hello from the main thread!");
}
تابع main یک رشته جدید با تابع thread::spawn ایجاد می کند و با عبور از یک بسته حاوی کد اجرا در thread (در این مورد، بسته شدن یک تابع ناشناس است). بسته شدن پیامی را چاپ می کند که نشان می دهد رشته جدید در حال اجرا است.
متد join در thread_handle به thread اصلی اجازه می دهد تا منتظر شود تا thread ساخته شده کامل شود. با فراخوانی join، تابع اطمینان میدهد که نخ اصلی قبل از ادامه، منتظر میماند تا نخ تخمگذاری شده تکمیل شود.
شما می توانید چندین نخ ایجاد کنید و از یک حلقه یا هر ساختار کنترل Rust دیگری برای ایجاد چندین بسته و نخ برای هر یک استفاده کنید.
use std::thread;
fn main() {
let num_threads = 5;
let mut thread_handles = vec![];
for i in 0..num_threads {
let thread_handle = thread::spawn(move || {
println!("Hello from thread {}", i);
});
thread_handles.push(thread_handle);
}
for handle in thread_handles {
handle.join().unwrap();
}
println!("All threads finished!");
}
حلقه for پنج رشته ایجاد می کند که هر کدام به یک شناسه منحصر به فرد i با متغیر حلقه اختصاص داده می شود. بستهها مقدار i را با کلمه کلیدی move میگیرند تا از مشکلات مالکیت جلوگیری کنند، و بردار thread_handles رشتهها را برای بعد در حلقه join ذخیره میکند.
پس از تخم ریزی همه رشته ها، تابع اصلی بر روی بردار thread_handles تکرار می شود، هر دسته را فراخوانی می کند و منتظر می ماند تا همه رشته ها اجرا شوند.
انتقال پیام ها از طریق کانال ها
شما می توانید پیام ها را از طریق رشته های دارای کانال ارسال کنید. Rust عملکردی را برای ارسال پیام در ماژول std::sync::mpsc فراهم می کند. در اینجا، mpsc مخفف “مولتیپل تولید کننده، مصرف کننده تک” است و اجازه می دهد تا ارتباط بین رشته های مختلف از طریق ارسال و دریافت پیام از طریق کانال ها برقرار شود.
در اینجا نحوه پیاده سازی پیام از طریق کانال های ارتباط بین رشته ای در برنامه های خود آورده شده است:
use std::sync::mpsc;
use std::thread;
fn main() {
// Create a channel
let (sender, receiver) = mpsc::channel();
// Spawn a thread
thread::spawn(move || {
// Send a message through the channel
sender.send("Hello from the thread!").unwrap();
});
// Receive the message in the main thread
let received_message = receiver.recv().unwrap();
println!("Received message: {}", received_message);
}
تابع main یک کانال با mpsc::channel () ایجاد می کند که یک فرستنده و یک گیرنده را برمی گرداند. فرستنده پیام هایی را برای گیرنده ای که پیام ها را دریافت می کند ارسال می کند. تابع اصلی به ایجاد رشته ها و انتقال مالکیت فرستنده به بسته شدن رشته ادامه می دهد. در داخل بسته شدن رشته، تابع sender.send() پیامی را از طریق کانال ارسال می کند.
تابع ()recever.recv پیام را با توقف اجرا تا زمانی که thread پیام را دریافت کند، دریافت می کند. تابع اصلی پیام را پس از دریافت موفقیت آمیز پیام در کنسول چاپ می کند.
توجه داشته باشید که ارسال پیام از طریق کانال باعث مصرف فرستنده می شود. اگر نیاز به ارسال پیام از چندین رشته دارید، می توانید فرستنده را با تابع ()sender.clone کلون کنید.
علاوه بر این، ماژول mpsc روشهای دیگری مانند try_recv() را ارائه میکند که بدون مسدود کردن آن سعی میکند پیامی را دریافت کند، و iter()، که یک تکرارکننده روی پیامهای دریافتشده ایجاد میکند.
ارسال پیام از طریق کانالها یک راه امن و راحت برای برقراری ارتباط بین رشتهها و در عین حال اجتناب از رقابت دادهها و اطمینان از همگامسازی مناسب فراهم میکند.
Rust’s Ownership and Borrowing Model ایمنی حافظه را تضمین می کند
Rust مالکیت، استقراض و بررسی کننده قرض را ترکیب می کند تا یک چارچوب برنامه نویسی قوی، ایمن و همزمان ارائه دهد.
چککننده قرض بهعنوان یک شبکه ایمنی عمل میکند و به جای اتکا به بررسیهای زمان اجرا یا جمعآوری زباله، مشکلات احتمالی را در زمان کامپایل تشخیص میدهد.