C++ में समवर्तीता और मल्टीथ्रेडिंग: उन्नत स्तर
\n\nआधुनिक अनुप्रयोगों को अधिकतम प्रदर्शन की आवश्यकता होती है, और C++ में मल्टीथ्रेडिंग इसे प्राप्त करने के लिए प्रमुख उपकरणों में से एक है। हालाँकि, बुनियादी तंत्र (std::thread, std::mutex) अक्सर ब्लॉकिंग, डेटा रेस और दक्षता में कमी का कारण बनते हैं। इस लेख में, हम C++ में समवर्तीता के साथ काम करने की उन्नत तकनीकों पर चर्चा करेंगे: परमाणु संक्रियाओं से लेकर lock-free डेटा संरचनाओं और एसिंक्रोनस कार्यों तक।
\n\n1. परमाणु संक्रियाएँ और memory ordering
\n\nक्लासिकल म्यूटेक्स एक भारी तंत्र हैं। सरल काउंटर या फ्लैग के लिए std::atomic का उपयोग करना बेहतर है। यह बिना लॉक के पढ़ने/लिखने की परमाणुता की गारंटी देता है। हालाँकि, उन्नत स्तर पर memory ordering को समझना महत्वपूर्ण है - C++ मेमोरी मॉडल, जो थ्रेड्स के बीच परिवर्तनों की दृश्यता निर्धारित करता है।
मुख्य मेमोरी ऑर्डर:
\n- \n
memory_order_relaxed— न्यूनतम गारंटी, केवल परमाणुता। \n memory_order_consume— शायद ही कभी उपयोग किया जाता है, डेटा पर निर्भर करता है। \n memory_order_acquire— इस संक्रिया से पहले सभी बाद के पढ़ने/लिखने को पुनः क्रमबद्ध नहीं किया जा सकता। \n memory_order_release— इस संक्रिया के बाद सभी पिछले लेखन अन्य थ्रेड्स को दिखाई देने लगते हैं। \n memory_order_acq_rel— acquire और release का संयोजन। \n memory_order_seq_cst— सबसे सख्त, वैश्विक अनुक्रमिक स्थिरता (डिफ़ॉल्ट)। \n
उपयोग का उदाहरण:
\n#include <atomic>\n#include <thread>\n#include <cassert>\n\nstd::atomic<bool> ready{false};\nint data = 0;\n\nvoid producer() {\n data = 42; // डेटा लिखना\n ready.store(true, std::memory_order_release); // फ्लैग प्रकाशित करना\n}\n\nvoid consumer() {\n while (!ready.load(std::memory_order_acquire)) // फ्लैग की प्रतीक्षा\n ;\n assert(data == 42); // गारंटी है कि data == 42 दिखेगा\n}\n\nint main() {\n std::thread t1(producer);\n std::thread t2(consumer);\n t1.join();\n t2.join();\n return 0;\n}\n\nयहाँ release और acquire गारंटी देते हैं कि data का लेखन फ्लैग सेट करने से पहले होगा, और पढ़ना बाद में होगा। यह म्यूटेक्स से तेज़ है।
2. Lock-free डेटा संरचनाएँ
\n\nLock-free एल्गोरिदम परमाणु संक्रियाओं और CAS (Compare-And-Swap) का उपयोग करके ब्लॉकिंग से बचते हैं। एक क्लासिकल उदाहरण एकल-लिंक्ड सूची पर आधारित lock-free स्टैक है।
\n\nLock-free स्टैक का कार्यान्वयन:
\n#include <atomic>\n#include <memory>\n\ntemplate<typename T>\nclass LockFreeStack {\nprivate:\n struct Node {\n T data;\n Node* next;\n Node(const T& val) : data(val), next(nullptr) {}\n };\n std::atomic<Node*> head{nullptr};\n\npublic:\n void push(const T& val) {\n Node* new_node = new Node(val);\n new_node->next = head.load(std::memory_order_relaxed);\n while (!head.compare_exchange_weak(\n new_node->next, new_node,\n std::memory_order_release,\n std::memory_order_relaxed)) {\n // लूप, जब तक CAS सफल न हो जाए\n }\n }\n\n bool pop(T& result) {\n Node* old_head = head.load(std::memory_order_relaxed);\n while (old_head != nullptr &&\n !head.compare_exchange_weak(\n old_head, old_head->next,\n std::memory_order_acquire,\n std::memory_order_relaxed)) {\n // प्रतीक्षा\n }\n if (old_head == nullptr) return false;\n result = old_head->data;\n // ध्यान दें: वास्तविक कोड में कचरा संग्रहक या hazard pointers की आवश्यकता है\n delete old_head;\n return true;\n }\n};\n\nमहत्वपूर्ण टिप्पणियाँ:
\n- \n