2009-03-18 12 views
48

यदि आपने गुई टूलकिट के साथ काम किया है, तो आप जानते हैं कि एक इवेंट-लूप/मेन-लूप है जिसे सब कुछ करने के बाद निष्पादित किया जाना चाहिए, और यह एप्लिकेशन को विभिन्न घटनाओं के लिए जीवंत और उत्तरदायी बनाए रखेगा। मुख्य उदाहरण के लिए, क्यूटी के लिए, तो आप इस में क्या करना होगा():आप एक बुनियादी घटना-लूप कैसे कार्यान्वित करेंगे?

int main() { 
    QApplication app(argc, argv); 
    // init code 
    return app.exec(); 
} 

जो इस मामले में, app.exec() आवेदन के मुख्य पाश है।

स्पष्ट तरीका पाश इस तरह लागू करने के लिए होगा:

void exec() { 
    while (1) { 
     process_events(); // create a thread for each new event (possibly?) 
    } 
} 

लेकिन यह 100% सीपीयू को सीमित करता है और व्यावहारिक रूप से बेकार है। अब, मैं ऐसे इवेंट लूप को कैसे कार्यान्वित कर सकता हूं जो सीपीयू को पूरी तरह से खाने के बिना उत्तरदायी है?

उत्तर पाइथन और/या सी ++ में सराहना की जाती है। धन्यवाद।

फुटनोट: सीखने के लिए, मैं अपने स्वयं के संकेत/स्लॉट लागू करूंगा, और मैं कस्टम ईवेंट उत्पन्न करने के लिए उन लोगों का उपयोग करूंगा (उदा। go_forward_event(steps))। लेकिन अगर आप जानते हैं कि मैं सिस्टम इवेंट मैन्युअल रूप से कैसे उपयोग कर सकता हूं, तो मैं इसके बारे में भी जानना चाहता हूं।

+3

मेरा मानना ​​है कि आप क्यूटी के स्रोत कोड में तल्लीन हैं और देखते हैं जैसा कि वे कार्यकारी में कर रहे हैं() कर सकते हैं। इससे आपको कुछ अच्छे पॉइंटर्स मिलेंगे। – JimDaniel

उत्तर

62

मैं इसके बारे में बहुत कुछ सोचता था!

एक जीयूआई मुख्य पाश इस तरह दिखता है, छद्म कोड में:

void App::exec() { 
    for(;;) { 
     vector<Waitable> waitables; 
     waitables.push_back(m_networkSocket); 
     waitables.push_back(m_xConnection); 
     waitables.push_back(m_globalTimer); 
     Waitable* whatHappened = System::waitOnAll(waitables); 
     switch(whatHappened) { 
      case &m_networkSocket: readAndDispatchNetworkEvent(); break; 
      case &m_xConnection: readAndDispatchGuiEvent(); break; 
      case &m_globalTimer: readAndDispatchTimerEvent(); break; 
     } 
    } 
} 

एक "Waitable" क्या है? खैर, यह प्रणाली निर्भर है। यूनिक्स पर इसे "फाइल डिस्क्रिप्टर" और "वेटऑनएल" कहा जाता है :: चयन प्रणाली कॉल है। तथाकथित vector<Waitable> यूनिक्स पर ::fd_set है, और "whatHappened" वास्तव में FD_ISSET के माध्यम से पूछताछ की जाती है। वास्तविक प्रतीक्षा योग्य हैंडल विभिन्न तरीकों से अधिग्रहित किए जाते हैं, उदाहरण के लिए m_xConnection :: XConnectionNumber() से लिया जा सकता है। एक्स 11 इस के लिए उच्च स्तरीय, पोर्टेबल एपीआई भी प्रदान करता है - :: XNextEvent() - लेकिन यदि आप इसका उपयोग करना चाहते हैं, तो आप कई घटना स्रोत पर एक साथ पर प्रतीक्षा नहीं कर पाएंगे।

अवरोधन कार्य कैसे करता है? "waitOnAll" एक सिस्कोल है जो ओएस को आपकी प्रक्रिया को "नींद सूची" पर रखने के लिए कहता है। इसका मतलब है कि जब तक कोई वेटेबल्स पर कोई घटना न हो तब तक आपको कोई CPU समय नहीं दिया जाता है। इसके बाद, इसका मतलब है कि आपकी प्रक्रिया निष्क्रिय है, 0% CPU का उपभोग कर रही है। जब कोई घटना होती है, तो आपकी प्रक्रिया संक्षिप्त रूप से प्रतिक्रिया देगी और फिर निष्क्रिय स्थिति पर वापस आ जाएगी।जीयूआई ऐप्स लगभग सभी अपना समय निष्क्रिय करते हैं।

सोते समय सभी CPU चक्रों का क्या होता है? निर्भर करता है। कभी-कभी एक और प्रक्रिया उनके लिए उपयोग करेगी। यदि नहीं, तो आपका ओएस सीपीयू व्यस्त-लूप करेगा, या इसे अस्थायी लो-पावर मोड आदि में रखेगा।

कृपया और जानकारी के लिए पूछें!

+0

मैं इस तरह के प्रतीक्षा प्रणाली को सिस्टम सिग्नल के लिए इंतजार करने के लिए कैसे लागू करूं, लेकिन मेरे अपने संकेतों के लिए? – fengshaun

+0

जैसा कि मैंने कहा था, आपका कोड केवल घटनाओं की प्रतिक्रिया में चलता है। इसलिए, यदि आप अपनी खुद की घटना को आग लगाते हैं, तो आप इसे कुछ सिस्टम इवेंट की प्रतिक्रिया के रूप में करने वाले हैं। और फिर यह स्पष्ट हो जाता है कि आपको वास्तव में अपने कस्टम कार्यक्रमों के लिए एक ईवेंट सिस्टम की आवश्यकता नहीं है। बस सीधे हैंडलर को बुलाओ! –

+0

उदाहरण के लिए, एक संकेत "बटन :: क्लिक किया गया" पर विचार करें। यह केवल सिस्टम इवेंट (बाएं माउस बटन रिलीज) के जवाब में आग लगने जा रहा है। तो आपका कोड "वर्चुअल शून्य बटन :: हैंडल लाइफ रिलीज (प्वाइंट) {clicked.invoke();}" थ्रेड या इवेंट कतार या कुछ भी की आवश्यकता के बिना बन जाता है। –

11

आम तौर पर मैं counting semaphore किसी प्रकार के साथ ऐसा होगा:

  1. सेमाफोर शून्य पर शुरू होता है।
  2. इवेंट लूप सेमफोर पर इंतजार कर रहा है।
  3. इवेंट (ओं) में आते हैं, सेमफोर बढ़ता है।
  4. इवेंट हैंडलर unblocks और semaphore में कमी और घटना को संसाधित करता है।
  5. जब सभी घटनाओं को संसाधित किया जाता है, तो सैमफोर शून्य होता है और ईवेंट लूप ब्लॉक फिर से होता है।

यदि आप उस जटिल को प्राप्त नहीं करना चाहते हैं, तो आप थोड़ी देर के नींद के समय के साथ अपने समय के दौरान एक नींद() कॉल जोड़ सकते हैं। इससे आपका संदेश प्रोसेसिंग थ्रेड अन्य सीपीयू के लिए इसके CPU समय को उत्पन्न करने का कारण बन जाएगा। सीपीयू को अब 100% पर नहीं देखा जाएगा, लेकिन यह अभी भी बहुत अपर्याप्त है।

+0

यह आकर्षक लगता है, मुझे थ्रेडिंग के बारे में और जानना होगा। धन्यवाद। – fengshaun

+0

तो हमें एक और थ्रेड चाहिए जो व्यस्त इंतजार करता है? –

+0

@FallingFromBed - व्यस्त प्रतीक्षा नहीं, लेकिन एक sepmaphore पर एक अवरुद्ध प्रतीक्षा। अंतर महत्वपूर्ण है क्योंकि अवरुद्ध प्रतीक्षा सीपीयू समय का उपभोग नहीं करेगा। –

10

मैं ज़ीरोएमक्यू (http://www.zeromq.org/) नामक एक साधारण, हल्के वजन वाली संदेश लाइब्रेरी का उपयोग करूंगा। यह एक ओपन सोर्स लाइब्रेरी (एलजीपीएल) है। यह एक बहुत छोटी पुस्तकालय है; मेरे सर्वर पर, पूरी परियोजना लगभग 60 सेकंड में संकलित होती है।

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

+3

वाह, यह अच्छा है। –

21

पायथन:

आप Twisted reactor के कार्यान्वयन जो शायद अजगर में एक घटना पाश के लिए सबसे अच्छा कार्यान्वयन है देख सकते हैं। ट्विस्ट में रिएक्टर एक इंटरफ़ेस के कार्यान्वयन हैं और आप चलाने के लिए एक प्रकार का रिएक्टर निर्दिष्ट कर सकते हैं: चयन, एपॉल, क्यूक्यू (सभी सिस्टम कॉल का उपयोग कर सी एपीआई पर आधारित), क्यूटी और जीटीके टूलकिट्स के आधार पर रिएक्टर भी हैं।

एक साधारण कार्यान्वयन का चयन का उपयोग करने के होगा:

#echo server that accepts multiple client connections without forking threads 

import select 
import socket 
import sys 

host = '' 
port = 50000 
backlog = 5 
size = 1024 
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
server.bind((host,port)) 
server.listen(backlog) 
input = [server,sys.stdin] 
running = 1 

#the eventloop running 
while running: 
    inputready,outputready,exceptready = select.select(input,[],[]) 

    for s in inputready: 

     if s == server: 
      # handle the server socket 
      client, address = server.accept() 
      input.append(client) 

     elif s == sys.stdin: 
      # handle standard input 
      junk = sys.stdin.readline() 
      running = 0 

     else: 
      # handle all other sockets 
      data = s.recv(size) 
      if data: 
       s.send(data) 
      else: 
       s.close() 
       input.remove(s) 
server.close() 
संबंधित मुद्दे