आधार
एक सरलीकृत उदाहरण से शुरू करें और संबंधित Boost.Asio टुकड़ों की जांच करें:
void handle_async_receive(...) { ... }
void print() { ... }
...
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket socket(io_service);
...
io_service.post(&print);
socket.connect(endpoint);
socket.async_receive(buffer, &handle_async_receive);
io_service.post(&print);
io_service.run();
हैंडलर क्या है ?
एक हैंडलर एक कॉलबैक से ज्यादा कुछ नहीं है। उदाहरण कोड में, 3 हैंडलर हैं:
print
हैंडलर (1)।
handle_async_receive
हैंडलर (3)।
print
हैंडलर (4)।
भले ही एक ही print()
फ़ंक्शन दो बार उपयोग किया जाता है, प्रत्येक उपयोग को अपना विशिष्ट पहचानकर्ता हैंडलर बनाने के लिए माना जाता है। हैंडलर कई आकार और आकारों में आ सकते हैं, जिनमें बुनियादी कार्यों से लेकर ऊपर से अधिक जटिल निर्माण जैसे कि लैंबडैस से उत्पन्न boost::bind()
फंक्शनलर्स शामिल हैं। जटिलता के बावजूद, हैंडलर अभी भी कॉलबैक से ज्यादा कुछ नहीं है।
काम क्या है ?
कार्य कुछ प्रसंस्करण है जिसे Boost.Asio ने आवेदन कोड की ओर से करने का अनुरोध किया है। कभी-कभी Boost.Asio कुछ काम शुरू कर सकता है जैसे ही इसके बारे में बताया गया है, और अन्य समय में यह काम करने के लिए प्रतीक्षा कर सकता है। एक बार जब यह काम पूरा हो जाता है, तो Boost.Asio आपूर्ति किए गए हैंडलर को आवेदन करके सूचित करेगा ।
Boost.Asio गारंटी देता है कि संचालकों केवल एक धागा है कि वर्तमान में बुला रहा है के भीतर चलेंगे run()
, run_one()
, poll()
, या poll_one()
। ये धागे हैं जो काम करेंगे और हैंडलर को बुलाएंगे । इसलिए, उपरोक्त उदाहरण में, print()
इसे io_service
(1) में पोस्ट किए जाने पर इनवोक नहीं किया जाता है । इसके बजाय, इसे जोड़ा जाता है io_service
और बाद में समय पर लागू किया जाएगा। इस मामले में, यह io_service.run()
(5) के भीतर है ।
अतुल्यकालिक संचालन क्या हैं?
एक एसिंक्रोनस ऑपरेशन कार्य बनाता है और Boost.Asio एक हैंडलर को आवेदन को सूचित करने के लिए आमंत्रित करेगा जब काम पूरा हो गया है। अतुल्यकालिक संचालन एक फ़ंक्शन को कॉल करके बनाए जाते हैं, जिसमें उपसर्ग के साथ एक नाम होता है async_
। इन कार्यों को आरंभ करने वाले कार्यों के रूप में भी जाना जाता है ।
अतुल्यकालिक संचालन तीन अद्वितीय चरणों में विघटित हो सकते हैं:
- आरंभ करने या सूचित करने के लिए, संबंधित
io_service
जो काम करता है उसे करने की आवश्यकता है। async_receive
आपरेशन (3) को सूचित io_service
है कि यह सॉकेट से एसिंक्रोनस रूप से पढ़ने के लिए डेटा की आवश्यकता होगी, तो async_receive
तुरंत वापस आती है।
- वास्तविक कार्य करना। इस स्थिति में, जब
socket
डेटा प्राप्त होता है, तो बाइट्स को पढ़ा और कॉपी किया जाएगा buffer
। वास्तविक काम या तो किया जाएगा:
- दीक्षा समारोह (3), अगर Boost.Asio यह निर्धारित कर सकता है कि यह ब्लॉक नहीं होगा।
- जब आवेदन स्पष्ट रूप से
io_service
(5) चलाते हैं ।
handle_async_receive
ReadHandler को आमंत्रित करना । एक बार फिर, हैंडलर्स को केवल थ्रेड चलाने के भीतर लगाया जाता है io_service
। इस प्रकार, जब भी काम किया जाता है (3 या 5), यह गारंटी है कि handle_async_receive()
केवल io_service.run()
(5) के भीतर लागू किया जाएगा ।
इन तीन चरणों के बीच समय और स्थान में अलगाव को नियंत्रण प्रवाह व्युत्क्रम के रूप में जाना जाता है। यह उन जटिलताओं में से एक है जो अतुल्यकालिक प्रोग्रामिंग को कठिन बनाता है। हालांकि, ऐसी तकनीकें हैं जो इसे कम करने में मदद कर सकती हैं, जैसे कि कोरटाइन का उपयोग करके ।
क्या करता है io_service.run()
?
जब एक थ्रेड कॉल होता है io_service.run()
, तो इस थ्रेड के भीतर से काम और हैंडलर मंगाए जाएंगे। उपरोक्त उदाहरण में, io_service.run()
(5) तब तक ब्लॉक रहेगा जब तक:
- यह दोनों
print
हैंडलर से मंगवाया गया है और वापस आ गया है , प्राप्त करने वाला ऑपरेशन सफलता या विफलता के साथ पूरा होता है, और इसके handle_async_receive
हैंडलर को वापस भेज दिया जाता है।
- के
io_service
माध्यम से स्पष्ट रूप से रोका जाता है io_service::stop()
।
- एक अपवाद को एक हैंडलर के भीतर से फेंक दिया जाता है।
एक संभावित पीडो-ईश प्रवाह को निम्नलिखित के रूप में वर्णित किया जा सकता है:
io_service बनाएं
सॉकेट बनाएं
प्रिंट हैंडलर को io_service (1) में जोड़ें
कनेक्ट करने के लिए सॉकेट की प्रतीक्षा करें (2)
io_service (3) में एक अतुल्यकालिक रीड वर्क अनुरोध जोड़ें
io_service (4) के लिए प्रिंट हैंडलर जोड़ें
io_service चलाएं (5)
वहाँ काम या हैंडलर है?
हाँ, 1 काम और 2 हैंडलर है
सॉकेट में डेटा है? नहीं, कुछ मत करो
रन प्रिंट हैंडलर (1)
वहाँ काम या हैंडलर है?
हां, 1 काम और 1 हैंडलर है
सॉकेट में डेटा है? नहीं, कुछ मत करो
रन प्रिंट हैंडलर (4)
वहाँ काम या हैंडलर है?
हां, 1 काम है
सॉकेट में डेटा है? नहीं, प्रतीक्षा जारी रखें
- सॉकेट डेटा प्राप्त करता है -
सॉकेट में डेटा है, इसे बफर में पढ़ें
io_service में हैंडल_आसंक_प्रतिकार हैंडलर जोड़ें
वहाँ काम या हैंडलर है?
हां, 1 हैंडलर है
रन हैंड_सुंक_सिव हैंडलर (3)
वहाँ काम या हैंडलर है?
नहीं, io_service को रोकें और वापस लौटें
ध्यान दें कि जब रीड समाप्त होता है, तो इसमें एक और हैंडलर जोड़ा जाता है io_service
। यह सूक्ष्म विवरण अतुल्यकालिक प्रोग्रामिंग की एक महत्वपूर्ण विशेषता है। यह संचालकों को एक साथ जंजीर बनाने की अनुमति देता है । उदाहरण के लिए, यदि handle_async_receive
यह अपेक्षित सभी डेटा नहीं मिला, तो इसका कार्यान्वयन एक अन्य अतुल्यकालिक रीड ऑपरेशन को पोस्ट कर सकता है, जिसके परिणामस्वरूप io_service
अधिक काम हो सकता है, और इस तरह से वापस नहीं लौटना होगा io_service.run()
।
ध्यान दें कि जब io_service
काम से बाहर चला गया है, तो इसे फिर से चलाने reset()
से io_service
पहले आवेदन करना होगा ।
उदाहरण प्रश्न और उदाहरण 3a कोड
अब, प्रश्न में संदर्भित कोड के दो टुकड़ों की जांच करने देता है।
प्रश्न संहिता
socket->async_receive
को काम जोड़ता है io_service
। इस प्रकार, io_service->run()
जब तक रीड ऑपरेशन सफलता या त्रुटि के साथ पूरा नहीं हो जाता है , तब तक ब्लॉक रहेगा, और ClientReceiveEvent
या तो दौड़ना समाप्त कर दिया है या एक अपवाद फेंकता है।
यह समझने में आसान बनाने की उम्मीद में, यहां एक छोटा एनोटेट उदाहरण 3 ए है:
void CalculateFib(std::size_t n);
int main()
{
boost::asio::io_service io_service;
boost::optional<boost::asio::io_service::work> work =
boost::in_place(boost::ref(io_service));
boost::thread_group worker_threads;
for(int x = 0; x < 2; ++x)
{
worker_threads.create_thread(
boost::bind(&boost::asio::io_service::run, &io_service)
);
}
io_service.post(boost::bind(CalculateFib, 3));
io_service.post(boost::bind(CalculateFib, 4));
io_service.post(boost::bind(CalculateFib, 5));
work = boost::none;
worker_threads.join_all();
}
उच्च-स्तर पर, प्रोग्राम 2 थ्रेड्स बनाएगा जो कि io_service
ईवेंट लूप (2) को प्रोसेस करेगा । इसका परिणाम एक साधारण थ्रेड पूल है जो फाइबोनैचि संख्या (3) की गणना करेगा।
प्रश्न कोड और इस कोड के बीच एक बड़ा अंतर यह है कि यह कोड वास्तविक कार्य से पहलेio_service::run()
(2) आक्रमण करता है और हैंडलर को (3) में जोड़ा जाता है । तुरंत लौटने से रोकने के लिए , एक ऑब्जेक्ट (1) बनाया जाता है। यह ऑब्जेक्ट काम से बाहर जाने से रोकता है ; इसलिए, बिना काम के परिणाम के रूप में वापस नहीं आएगा।io_service
io_service::run()
io_service::work
io_service
io_service::run()
समग्र प्रवाह निम्नानुसार है:
io_service::work
इसमें जोड़े गए ऑब्जेक्ट को बनाएं और जोड़ें io_service
।
- थ्रेड पूल ने उस आक्रमण को बनाया
io_service::run()
। ये वर्कर थ्रेड ऑब्जेक्ट के io_service
कारण से वापस नहीं आएंगे io_service::work
।
- 3 हैंडलर जोड़ें जो फाइबोनैचि संख्याओं की गणना करते हैं
io_service
, और तुरंत वापस लौटते हैं। श्रमिक सूत्र, मुख्य धागा नहीं, इन हैंडलर को तुरंत चलाना शुरू कर सकते हैं।
- हटाएँ
io_service::work
वस्तु।
- रनिंग खत्म करने के लिए वर्कर थ्रेड्स का इंतजार करें। यह केवल एक बार होगा जब सभी 3 हैंडलर ने निष्पादन समाप्त कर दिया है, क्योंकि
io_service
न तो हैंडलर हैं और न ही काम करते हैं।
कोड को अलग-अलग तरीके से लिखा जा सकता है, मूल कोड के समान, जहां हैंडलर जोड़े जाते हैं io_service
, और फिर io_service
इवेंट लूप संसाधित होता है। यह उपयोग करने की आवश्यकता को हटाता है io_service::work
, और निम्न कोड में परिणाम:
int main()
{
boost::asio::io_service io_service;
io_service.post(boost::bind(CalculateFib, 3));
io_service.post(boost::bind(CalculateFib, 4));
io_service.post(boost::bind(CalculateFib, 5));
boost::thread_group worker_threads;
for(int x = 0; x < 2; ++x)
{
worker_threads.create_thread(
boost::bind(&boost::asio::io_service::run, &io_service)
);
}
worker_threads.join_all();
}
सिंक्रोनस बनाम एसिंक्रोनस
यद्यपि प्रश्न में कोड एक अतुल्यकालिक ऑपरेशन का उपयोग कर रहा है, यह प्रभावी ढंग से तुल्यकालिक रूप से काम कर रहा है, क्योंकि यह अतुल्यकालिक ऑपरेशन पूरा होने की प्रतीक्षा कर रहा है:
socket.async_receive(buffer, handler)
io_service.run();
के बराबर है:
boost::asio::error_code error;
std::size_t bytes_transferred = socket.receive(buffer, 0, error);
handler(error, bytes_transferred);
अंगूठे के एक सामान्य नियम के रूप में, सिंक्रोनस और एसिंक्रोनस ऑपरेशन को मिलाने से बचने की कोशिश करें। अक्सर बार, यह एक जटिल प्रणाली को एक जटिल प्रणाली में बदल सकता है। यह उत्तर अतुल्यकालिक प्रोग्रामिंग के लाभों पर प्रकाश डालता है, जिनमें से कुछ Boost.Asio प्रलेखन में भी शामिल हैं ।