परिचय: Rust में कार्य कतारों की आवश्यकता क्यों है?
\nRust में एप्लिकेशन विकसित करते समय, अक्सर कार्यों को एसिंक्रोनस रूप से संसाधित करने या कई थ्रेड्स के बीच लोड वितरित करने की आवश्यकता उत्पन्न होती है। कार्य कतार (task queue) एक मौलिक डेटा संरचना है जो कार्य इकाइयों के अनुक्रमिक या समवर्ती प्रसंस्करण को व्यवस्थित करने की अनुमति देती है। इस लेख में, हम सरल उदाहरणों से शुरू करके वास्तविक परिदृश्यों तक, Rust में कार्य कतारें बनाने और उपयोग करने का तरीका समझेंगे।
\nRust कतारों को लागू करने के लिए कई दृष्टिकोण प्रदान करता है: std::sync::mpsc से मानक चैनलों से लेकर tokio पर आधारित उन्नत एसिंक्रोनस समाधानों तक। हम उनमें से प्रत्येक पर विचार करेंगे और दिखाएंगे कि आपके कार्य के लिए सही उपकरण कैसे चुनें।
मूल बातें: mpsc चैनलों के माध्यम से कार्य कतार
\nRust में कार्य कतार बनाने का सबसे सरल तरीका मानक लाइब्रेरी से मल्टी-प्रोड्यूसर, सिंगल-कंज्यूमर चैनल (mpsc) का उपयोग करना है। यह तंत्र उन स्थितियों के लिए आदर्श है जहां एक थ्रेड कई प्रोड्यूसर थ्रेड्स से भेजे गए कार्यों को संसाधित करता है।
\n\nसबसे सरल कतार बनाना
\nएक बुनियादी उदाहरण पर विचार करें, जहां मुख्य थ्रेड कार्य भेजता है और एक अलग थ्रेड उन्हें संसाधित करता है:
\nuse std::sync::mpsc;\nuse std::thread;\nuse std::time::Duration;\n\nfn main() {\n // चैनल बनाएं: प्रेषक और प्राप्तकर्ता\n let (tx, rx) = mpsc::channel::<String>();\n\n // हैंडलर थ्रेड शुरू करें\n let handler = thread::spawn(move || {\n for task in rx {\n println!("कार्य संसाधित कर रहा हूँ: {}", task);\n thread::sleep(Duration::from_millis(500));\n }\n });\n\n // कार्य भेजें\n let tasks = vec!["कार्य 1", "कार्य 2", "कार्य 3"];\n for task in tasks {\n tx.send(task.to_string()).unwrap();\n println!("भेजा गया: {}", task);\n }\n\n // हैंडलर को समाप्त करने के लिए चैनल बंद करें\n drop(tx);\n handler.join().unwrap();\n}\nयह कोड बुनियादी यांत्रिकी प्रदर्शित करता है: tx.send() कार्य को कतार में रखता है, और प्राप्तकर्ता rx उन्हें आने के क्रम (FIFO) में निकालता है। ध्यान दें कि यदि कतार भरी हुई है तो चैनल स्वचालित रूप से प्रोड्यूसर को ब्लॉक करता है, और यदि कतार खाली है तो कंज्यूमर को ब्लॉक करता है।
एकाधिक प्रोड्यूसर
\nmpsc का एक मुख्य लाभ कई थ्रेड्स के लिए प्रेषक को क्लोन करने की क्षमता है:
\nuse std::sync::mpsc;\nuse std::thread;\n\nfn main() {\n let (tx, rx) = mpsc::channel::<u32>();\n let mut handles = vec![];\n\n // 3 प्रोड्यूसर थ्रेड शुरू करें\n for id in 0..3 {\n let tx_clone = tx.clone();\n handles.push(thread::spawn(move || {\n for i in 0..5 {\n let task = id * 100 + i;\n tx_clone.send(task).unwrap();\n }\n }));\n }\n\n // मूल प्रेषक को बंद करें\n drop(tx);\n\n // सभी कार्यों को संसाधित करें\n for received in rx {\n println!("कार्य प्राप्त हुआ: {}", received);\n }\n\n for handle in handles {\n handle.join().unwrap();\n }\n}\nप्रत्येक प्रोड्यूसर अपने कार्य भेजता है, और एक एकल कंज्यूमर उन्हें आने के क्रम में संसाधित करता है। यह क्लासिक "प्रोड्यूसर-कंज्यूमर" पैटर्न है।
\n\nप्राथमिकताओं के साथ उन्नत कतार
\nमानक mpsc चैनल प्राथमिकताओं का समर्थन नहीं करता है - कार्यों को भेजने के क्रम में सख्ती से संसाधित किया जाता है। हालांकि, हम मानक लाइब्रेरी से BinaryHeap का उपयोग करके प्राथमिकताओं वाली कतार लागू कर सकते हैं।
प्राथमिकता कतार का कार्यान्वयन
\nuse std::collections::BinaryHeap;\nuse std::cmp::Ordering;\n\n#[derive(Debug, Eq, PartialEq)]\nstruct Task {\n priority: u8,\n data: String,\n}\n\nimpl Ord for Task {\n fn cmp(&self, other: &Self) -> Ordering {\n // संख्या जितनी छोटी, प्राथमिकता उतनी अधिक\n other.priority.cmp(&self