2009-05-19 12 views
19

के लिए साफ कोड जबकि एसिंक्रोनस आईओ (चुनिंदा/पोल/एपोल/क्यूक्यू इत्यादि के साथ गैर-अवरुद्ध वर्णनकर्ता) वेब पर सबसे अधिक दस्तावेज की बात नहीं है, वहां कुछ अच्छे उदाहरण हैं।एसिंक्रोनस आईओ

हालांकि, इन सभी उदाहरणों ने कॉल द्वारा लौटाए गए हैंडल को निर्धारित किया है, बस 'do_some_io(fd)' स्टब है। वे वास्तव में इस तरह की विधि में वास्तविक एसिंक्रोनस आईओ से सर्वोत्तम तरीके से कैसे पहुंचने की व्याख्या नहीं करते हैं।

ब्लॉकिंग आईओ कोड पढ़ने के लिए बहुत साफ और सीधा है। गैर-अवरुद्ध, एसिंक आईओ दूसरी तरफ, बालों वाली और गन्दा है।

क्या दृष्टिकोण हैं? मजबूत और पठनीय क्या हैं?

void do_some_io(int fd) { 
    switch(state) { 
    case STEP1: 
     ... async calls 
     if(io_would_block) 
      return; 
     state = STEP2; 
    case STEP2: 
     ... more async calls 
     if(io_would_block) 
      return; 
     state = STEP3; 
    case STEP3: 
     ... 
    } 
} 

या शायद (ab) जीसीसी के अभिकलन gotos का उपयोग कर:

#define concatentate(x,y) x##y 
#define async_read_xx(var,bytes,line)  \ 
    concatentate(jmp,line):     \ 
    if(!do_async_read(bytes,&var)) {   \ 
     schedule(EPOLLIN);     \ 
     jmp_read = &&concatentate(jmp,line); \ 
     return;        \ 
} 

// macros for making async code read like sync code 
#define async_read(var,bytes) \ 
    async_read_xx(var,bytes,__LINE__) 

#define async_resume()   \ 
    if(jmp_read) {    \ 
     void* target = jmp_read; \ 
     jmp_read = NULL;   \ 
     goto *target;   \ 
    } 

void do_some_io() { 
    async_resume(); 
    async_read(something,sizeof(something)); 
    async_read(something_else,sizeof(something_else)); 
} 

या शायद सी ++ अपवाद और एक राज्य मशीन है, इसलिए कार्यकर्ता कार्यों बीच में बंद करें/फिर से शुरू बिट, या शायद एक मेज पर ही आधारित ट्रिगर कर सकते हैं राज्य मशीन?

यह काम नहीं करने का तरीका है, यह कैसे बनाए रखने योग्य है कि मैं पीछा कर रहा हूं!

उत्तर

16

मेरा सुझाव है कि: http://www.kegel.com/c10k.html पर एक नज़र डालें, दूसरा, मौजूदा पुस्तकालयों जैसे कि libevent, Boost.Asio पर एक नज़र डालें जो पहले से ही नौकरी करते हैं और देखते हैं कि वे कैसे काम करते हैं।

  • चयन है सरल रिएक्टर
  • epoll दोनों किनारे या स्तर इंटरफ़ेस है कि अलग दृष्टिकोण
  • iocp है की आवश्यकता होती है ट्रिगर किया है:

    मुद्दा यह है कि दृष्टिकोण सिस्टम कॉल के प्रत्येक प्रकार के लिए अलग-अलग हो सकता है proactor की आवश्यकता होती है अन्य दृष्टिकोण

सुझाव: सी

+०१२३५१६४१०६ के लिए सी के लिए Boost.Asio की तरह अच्छा मौजूदा पुस्तकालय ++ या libevent का उपयोग

संपादित करें: इस वजह से ASIO proactor के रूप में यह आपको सूचित आपरेशन पूरा हो गया है और आंतरिक EWOULDBLOCK संभालती है काम करता है कैसे ASIO इस

class connection { 
    boost::asio:ip::tcp::socket socket_; 
public: 
    void run() 
    { 
     // for variable length chunks 
     async_read_until(socket_,resizable_buffer,'\n', 
       boost::bind(&run::on_line_recieved,this,errorplacehplder); 
     // or constant length chunks 
     async_read(socket_,buffer(some_buf,buf_size), 
       boost::bind(&run::on_line_recieved,this,errorplacehplder); 
    } 
    void on_line_recieved(error e) 
    { 
     // handle it 
     run(); 
    } 

}; 

संभालती है।

यदि आप शब्द रिएक्टर के रूप में आप इस व्यवहार अनुकरण कर सकते हैं:

class conn { 
    // Application logic 

    void run() { 
     read_chunk(&conn::on_chunk_read,size); 
    } 
    void on_chunk_read() { 
     /* do something;*/ 
    } 

    // Proactor wrappers 

    void read_chunk(void (conn::*callback),int size, int start_point=0) { 
     read(socket,buffer+start,size) 
     if(complete) 
      (this->*callback() 
     else { 
      this -> tmp_size-=size-read; 
      this -> tmp_start=start+read; 
      this -> tmp_callback=callback 
      your_event_library_register_op_on_readable(callback,socket,this); 
     } 
    } 
    void callback() 
    { 
     read_chunk(tmp_callback,tmp_size,tmp_start); 
    } 
} 

कुछ ऐसे ही।

+0

libevent बफ़र घटनाओं रैपर अच्छा लगेगा, , किरकिरा आईओ हैंडलिंग से बचने का एक सुविधाजनक तरीका है; लेकिन आप इस स्थिति को उस कोड में कैसे प्रस्तुत करते हैं जो इसे कॉल करता है जिसे पुन: शुरू किया जाना चाहिए? – Will

+0

संपादित किया गया: एक उदाहरण जोड़ा गया – Artyom

+0

धन्यवाद Artyom, मुझे आशा है कि लोग एपोल को गुगलते हैं और ऐसा पाते हैं! – Will

5

राज्य मशीनें एक अच्छा दृष्टिकोण है। यह सामने की जटिलता है जो भविष्य में आपको सिरदर्द बचाएगी, जहां भविष्य वास्तव में वास्तव में शुरू होता है। ;-)

अन्य विधि धागे का उपयोग करना और प्रत्येक थ्रेड में एक ही एफडी पर I/O अवरुद्ध करना है। यहां व्यापार-बंद यह है कि आप I/O को सरल बनाते हैं लेकिन सिंक्रनाइज़ेशन में जटिलता पेश कर सकते हैं।

+11

async के लिए एक राज्य मशीन का एक त्वरित उदाहरण कब उपयोगी होगा – Will

+0

मैं भी उच्च और निम्न वॉटरमार्क के साथ कि उदाहरण देखने के लिए भी – Viet

0

आप प्रसंस्करण से "io" को अपनाना चाहते हैं, जिस बिंदु पर आप पढ़े गए कोड को बहुत पठनीय हो जाएगा।मूल रूप से आप:


    int read_io_event(...) { /* triggers when we get a read event from epoll/poll/whatever */ 

    /* read data from "fd" into a vstr/buffer/whatever */ 

    if (/* read failed */) /* return failure code to event callback */ ; 

    if (/* "message" received */) return process_io_event(); 

    if (/* we've read "too much" */) /* return failure code to event callback */ ; 

    return /* keep going code for event callback */ ; 
    } 


    int process_io_event(...) { 
     /* this is where you process the HTTP request/whatever */ 
    } 

... तो वास्तविक कोड प्रक्रिया घटना में है, और यहां तक ​​कि अगर आप एक से अधिक अनुरोध प्रतिक्रिया देते हैं यह बहुत पठनीय है, एक राज्य या जो कुछ भी स्थापित करने के बाद आप बस कर "वापस read_io_event()" ।

+2

एक बफर का निर्माण लाइन या संदेश स्तर पर पर्याप्त रूप से पर्याप्त काम करता है; लेकिन जब आप कुछ जटिल कर रहे हैं जो अधिक जटिल है, तो आप process_io_event() हैंडलर में इस स्थिति का प्रतिनिधित्व कैसे करते हैं? – Will

3

इस समस्या को हल करने के लिए महान डिजाइन पैटर्न "कोरआउट" मौजूद है।

यह दोनों दुनिया में सबसे अच्छा है: सिंक्रोनस आईओ प्रवाह की तरह साफ कोड और संदर्भ स्विचिंग के बिना शानदार प्रदर्शन, जैसे async io देता है। कोरआउटिन एक निर्देशक सूचक के साथ, एक मूत्र सिंक्रोनस थ्रेड की तरह अंदर दिखता है। लेकिन कई कोरआउट एक ओएस थ्रेड (तथाकथित "सहकारी मल्टीटास्किंग") के भीतर चल सकते हैं।

उदाहरण coroutine कोड:

void do_some_io() { 
    blocking_read(something,sizeof(something)); 
    blocking_read(something_else,sizeof(something_else)); 
    blocking_write(something,sizeof(something)); 
} 

तुल्यकालिक कोड की तरह लग रहा है, लेकिन तथ्य यह है नियंत्रण प्रवाह में इस तरह का एक और तरीका इस्तेमाल करते हैं,:

void do_some_io() { 
    // return control to network io scheduler, to handle another coroutine 
    blocking_read(something,sizeof(something)); 
    // when "something" is read, scheduler fill given buffer and resume this coroutine 

    // return control to network io scheduler, to handle another coroutine 
    CoroSleep(1000); 
    // scheduler create async timer and when it fires, scheduler pass control to this coroutine 
    ... 
    // and so on 

तो एकल थ्रेड अनुसूचक नियंत्रण उपयोगकर्ता परिभाषित के साथ कई coroutines कोड और साफ सिंक्रोनस-जैसे कॉल IO को।

सी ++ coroutines कार्यान्वयन उदाहरण "boost.coroutine" (वास्तव में नहीं एक बढ़ावा के हिस्से :) http://www.crystalclearsoftware.com/soc/coroutine/ इस पुस्तकालय पूरी तरह से coroutine यांत्रिकी लागू करता है और boost.asio अनुसूचक और async कब परत के रूप में उपयोग कर सकते हैं।

+0

[बूस्ट.कोराउटिन] (http://www.boost.org/doc/libs/release/libs/coroutine/doc/html/index.html) अब बढ़ावा का हिस्सा है। – Mankarse

1

आपको मुख्य लूप होना चाहिए जो async_schedule(), async_foreach(), async_tick() इत्यादि प्रदान करता है। ये फ़ंक्शन बदले में विधियों की वैश्विक सूची में प्रविष्टियों को स्थानांतरित करते हैं जो अगली कॉल पर async_tick() पर चलेंगे। फिर आप कोड लिख सकते हैं जो बहुत अधिक साफ है और इसमें कोई भी स्विच स्टेटमेंट शामिल नहीं है।

तुम बस लिख सकते हैं:

async_schedule(callback, arg, timeout); 

या:

async_wait(condition, callback, arg, timeout); 

फिर अपनी हालत भी (बशर्ते कि आप जब उस चर तक पहुँचने धागा सुरक्षा का ख्याल रखना) एक और धागा में सेट किया जा सकता है।

मैंने अपने एम्बेडेड प्रोजेक्ट के लिए सी में एसिंक फ्रेमवर्क लागू किया है क्योंकि मैं गैर-प्रीपेप्टिव मल्टीटास्किंग और एसिंक मुख्य लूप के प्रत्येक पुनरावृत्ति के दौरान थोड़ा सा काम करके कई कार्यों को करने के लिए एकदम सही है।

कोड यहाँ है: https://github.com/mkschreder/fortmax-blocks/blob/master/common/kernel/async.c

+1

लिंक अब काम नहीं करता है। –

संबंधित मुद्दे