Rust में समवर्तीता और मल्टीथ्रेडिंग: A से Z तक
\n\nआधुनिक एप्लिकेशन तेजी से मल्टी-कोर प्रोसेसर के कुशल उपयोग की मांग कर रहे हैं। Rust समवर्तीता और मल्टीथ्रेडिंग के लिए एक अनूठा दृष्टिकोण प्रदान करता है, जो संकलन चरण में मेमोरी सुरक्षा की गारंटी के साथ उच्च प्रदर्शन को जोड़ता है। इस लेख में, हम मुख्य अवधारणाओं को समझेंगे: थ्रेड बनाने से लेकर म्यूटेक्स और चैनल के उपयोग तक, व्यावहारिक कोड उदाहरणों के साथ।
\n\n1. मल्टीथ्रेडिंग की मूल बातें: थ्रेड बनाना और प्रबंधित करना
\n\nRust में, थ्रेड मानक लाइब्रेरी std::thread का उपयोग करके बनाए जाते हैं। प्रत्येक थ्रेड स्वतंत्र रूप से निष्पादित होता है, लेकिन अन्य थ्रेड के साथ डेटा साझा कर सकता है।
थ्रेड बनाने का एक सरल उदाहरण:
\n\nuse std::thread;\nuse std::time::Duration;\n\nfn main() {\n let handle = thread::spawn(|| {\n for i in 1..10 {\n println!("थ्रेड: संख्या {}", i);\n thread::sleep(Duration::from_millis(1));\n }\n });\n\n for i in 1..5 {\n println!("मुख्य थ्रेड: संख्या {}", i);\n thread::sleep(Duration::from_millis(1));\n }\n\n handle.join().unwrap(); // थ्रेड के समाप्त होने की प्रतीक्षा करें\n}\n\nमुख्य बिंदु:
\n- \n
thread::spawnएक क्लोजर लेता है जो नए थ्रेड में निष्पादित होता है। \n joinविधि वर्तमान थ्रेड को निर्दिष्ट थ्रेड के समाप्त होने तक ब्लॉक करती है। \n joinके बिना, मुख्य थ्रेड चाइल्ड थ्रेड से पहले समाप्त हो सकता है। \n
थ्रेड के बीच डेटा स्थानांतरित करने के लिए move के साथ क्लोजर का उपयोग करें:
use std::thread;\n\nfn main() {\n let data = vec![1, 2, 3, 4, 5];\n\n let handle = thread::spawn(move || {\n println!("थ्रेड से डेटा: {:?}", data);\n });\n\n handle.join().unwrap();\n // println!("{:?}", data); // त्रुटि! data पहले ही स्थानांतरित हो चुका है\n}\n\nmove कीवर्ड क्लोजर में डेटा का स्वामित्व स्थानांतरित करता है, संकलन चरण में डेटा रेस को रोकता है।
2. सुरक्षित डेटा साझाकरण: म्यूटेक्स और लॉक
\n\nजब कई थ्रेड साझा डेटा तक पहुँचते हैं, तो डेटा रेस का जोखिम होता है। Rust म्यूटेक्स (Mutex) और परमाणु प्रकारों का उपयोग करके इस समस्या का समाधान करता है।
Arc (परमाणु संदर्भ काउंटर) के साथ Mutex का उपयोग करने का उदाहरण:
use std::sync::{Arc, Mutex};\nuse std::thread;\n\nfn main() {\n let counter = Arc::new(Mutex::new(0));\n let mut handles = vec![];\n\n for _ in 0..10 {\n let counter = Arc::clone(&counter);\n let handle = thread::spawn(move || {\n let mut num = counter.lock().unwrap();\n *num += 1;\n });\n handles.push(handle);\n }\n\n for handle in handles {\n handle.join().unwrap();\n }\n\n println!("परिणाम: {}", *counter.lock().unwrap()); // 10\n}\n\nमहत्वपूर्ण विवरण:
\n- \n
Arc<T>कई थ्रेड को एक ही डेटा का स्वामित्व रखने की अनुमति देता है (परमाणु क्लोनिंग)। \n Mutex::lock()एक लॉक लौटाता है; यदि लॉक पहले से लिया हुआ है, तो थ्रेड प्रतीक्षा करता है। \n - स्कोप से बाहर निकलने पर लॉक स्वचालित रूप से जारी हो जाता है। \n
अधिक सूक्ष्म नियंत्रण के लिए RwLock का उपयोग करें, जो पाठकों और लेखकों को अलग करता है:
use std::sync::RwLock;\n\nlet lock = RwLock::new(5);\n\n// पढ़ना: एक साथ कई थ्रेड\n{\n let r1 = lock.read().unwrap();\n let r2 = lock.read().unwrap();\n println!("पढ़ रहे हैं: {} और {}", r1, r2);\n}\n\n// लिखना: केवल एक थ्रेड\n{\n let mut w = lock.write().unwrap();\n *w += 1;\n println!("लिखा: {}", w);\n}\n\n3. संदेश भेजने के लिए चैनल
\n\nसाझा मेमोरी का विकल्प — चैनल का उपयोग करके एक्टर मॉडल। Rust एक मल्टी-प्रोड्यूसर, सिंगल-कंज्यूमर चैनल (mpsc) प्रदान करता है।
मूल उदाहरण:
\n\nu