C++ में समवर्तीता और मल्टीथ्रेडिंग का परिचय
आधुनिक अनुप्रयोगों को अधिकतम प्रदर्शन की आवश्यकता होती है। C++ में समवर्तीता और मल्टीथ्रेडिंग मल्टी-कोर प्रोसेसर का कुशलतापूर्वक उपयोग करने की अनुमति देते हैं, जिससे कार्यों का निष्पादन तेज होता है। इन अवधारणाओं की समझ के बिना उच्च-लोड सिस्टम, डेटा प्रोसेसर या गेम इंजन बनाना संभव नहीं है।
इस लेख में, हम C++ में मल्टीथ्रेडिंग के प्रमुख तंत्रों का विश्लेषण करेंगे, बुनियादी std::thread से शुरू करके उन्नत पैटर्न तक। आप सीखेंगे कि डेटा रेस से कैसे बचें, थ्रेड्स को सही ढंग से कैसे सिंक्रोनाइज़ करें और सुरक्षित समानांतर कोड कैसे लिखें।
मल्टीथ्रेडिंग की मूल बातें: std::thread और std::jthread
क्लास std::thread (C++11) मल्टीथ्रेडेड प्रोग्रामिंग की नींव है। यह एक अलग थ्रेड में किसी फंक्शन या callable-ऑब्जेक्ट को चलाने की अनुमति देता है। आइए एक सरल उदाहरण देखें:
#include <iostream>#include <thread>
void worker(int id) { std::cout << "थ्रेड " << id << " ने काम शुरू किया"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "थ्रेड " << id << " पूरा हुआ";}
int main() { std::thread t1(worker, 1); std::thread t2(worker, 2); t1.join(); t2.join(); return 0;}मुख्य बिंदु:
- join() — मुख्य थ्रेड चाइल्ड थ्रेड के पूरा होने की प्रतीक्षा करता है
- detach() — थ्रेड स्वतंत्र रूप से काम करना जारी रखता है (संसाधन लीक का जोखिम)
- std::jthread (C++20) — डिस्ट्रक्टर में स्वचालित रूप से join() को कॉल करता है
डबल अटैचमेंट से बचने के लिए join() को कॉल करने से पहले हमेशा joinable() की जाँच करें।
थ्रेड सिंक्रोनाइज़ेशन: म्यूटेक्स और लॉक
मल्टीथ्रेडिंग की मुख्य समस्या डेटा रेस है, जब कई थ्रेड एक साथ एक साझा वेरिएबल को बदलते हैं। सुरक्षा के लिए म्यूटेक्स का उपयोग किया जाता है।
std::mutex और std::lock_guard
#include <iostream>#include <thread>#include <mutex>
std::mutex mtx;int counter = 0;
void increment() { for (int i = 0; i < 100000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; }}
int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "परिणाम: " << counter << ""; // 200000 return 0;}std::lock_guard स्कोप से बाहर निकलने पर स्वचालित रूप से म्यूटेक्स को मुक्त करता है। अधिक लचीले नियंत्रण के लिए std::unique_lock का उपयोग करें।
डेडलॉक और std::scoped_lock
डेडलॉक तब होता है जब दो थ्रेड एक-दूसरे की प्रतीक्षा करते हैं। समाधान std::scoped_lock (C++17) का उपयोग करना है, जो कई म्यूटेक्स को परमाणु रूप से कैप्चर करता है:
std::mutex m1, m2;
void safe_function() { std::scoped_lock lock(m1, m2); // दो संसाधनों के साथ सुरक्षित कार्य}समानांतर एल्गोरिदम और std::async
थ्रेड्स के मैन्युअल प्रबंधन के बिना एसिंक्रोनस कार्यों को निष्पादित करने के लिए std::async का उपयोग करें। यह std::future लौटाता है, जिससे परिणाम प्राप्त किया जा सकता है।
#include <iostream>#include <future>#include <chrono>
int compute(int n) { std::this_thread::sleep_for(std::chrono::seconds(n)); return n * n;}
int main() { auto future1 = std::async(std::launch::async, compute, 2); auto future2 = std::async(std::launch::async, compute, 3); std::cout << "परिणाम: " << future1.get() << ", " << future2.get() << ""; // 4, 9 return 0;}लॉन्च पॉलिसी:
std::launch::async— नए थ्रेड में लॉन्च करेंstd::launch::deferred— आलसी मूल्यांकन (जब get() कॉल किया जाए)