2015-04-25 11 views
7

मैं विशेष रूप से सॉकेट का उपयोग करने के साथ एसिन्सीओ लाइब्रेरी को समझने की कोशिश कर रहा हूं। मैंने समझने के प्रयास में कुछ कोड लिखा है,पायथन एसिंसिओ रन इवेंट लूप एक बार?

मैं एक प्रेषक और एक रिसीवर सॉकेट को अनजाने में चलाने के लिए चाहता था। मैं उस बिंदु पर पहुंचा जहां मुझे अंतिम डेटा तक सभी डेटा भेजा गया था, लेकिन फिर मुझे एक और लूप चलाने की ज़रूरत है। यह कैसे करना है, मुझे this link from stackoverflow मिला, जिसे मैंने नीचे कार्यान्वित किया - लेकिन यहां क्या हो रहा है? stop पर कॉल करने के बजाय run_forever पर कॉल करने के लिए ऐसा करने के लिए कोई बेहतर/अधिक सौहार्दपूर्ण तरीका है?

घटना पाश में stop() के लिए दस्तावेज़ है: घटना पाश चल

बंद करो।

स्टॉप() से पहले निर्धारित प्रत्येक कॉलबैक को चलाया जाएगा। स्टॉप() के बाद निर्धारित कॉलबैक को नहीं चलाया जाएगा। हालांकि, अगर कॉल_वर्वर() को बाद में बुलाया जाता है तो वे कॉलबैक चलाए जाएंगे।

और run_forever() के प्रलेखन है: रोक जब तक

रन() कहा जाता है।

सवाल:

  • क्यों दुनिया में run_foreverrun_once लिए एक ही रास्ता है? यह भी समझ में नहीं आता
  • क्या ऐसा करने का कोई बेहतर तरीका है?
  • क्या मेरा कोड asyncio लाइब्रेरी के साथ प्रोग्राम करने के लिए एक उचित तरीका की तरह दिखता है?
  • क्या asyncio.async() के अलावा ईवेंट लूप में कार्यों को जोड़ने का कोई बेहतर तरीका है? loop.create_task मेरे लिनक्स सिस्टम पर एक त्रुटि देता है।

https://gist.github.com/cloudformdesign/b30e0860497f19bd6596

उत्तर

10

stop(); run_forever() चाल क्योंकि stop कैसे कार्यान्वित किया जाता है की काम करता है:

def stop(self): 
    """Stop running the event loop. 

    Every callback scheduled before stop() is called will run. 
    Callback scheduled after stop() is called won't. However, 
    those callbacks will run if run() is called again later. 
    """ 
    self.call_soon(_raise_stop_error) 

def _raise_stop_error(*args): 
    raise _StopError 

तो, अगली बार घटना पाश रन और लंबित कॉलबैक कार्यान्वित करता है, यह _raise_stop_error कॉल करने के लिए है, जो जन्म देती हो रहा है _StopErrorrun_forever पाश केवल कि विशिष्ट अपवाद पर टूट जाएगा:

def run_forever(self): 
    """Run until stop() is called.""" 
    if self._running: 
     raise RuntimeError('Event loop is running.') 
    self._running = True 
    try: 
     while True: 
      try: 
       self._run_once() 
      except _StopError: 
       break 
    finally: 
     self._running = False 

तो, एक stop() का समय निर्धारण और फिर बुला run_forever द्वारा, आप अंत घटना पाश में से एक यात्रा चल रहा है, तो रोक एक बार यह _raise_stop_error कॉलबैक हिट। आपने यह भी देखा होगा कि _run_once को परिभाषित किया गया है और run_forever द्वारा बुलाया गया है। आप इसे सीधे कॉल कर सकते हैं, लेकिन अगर कभी भी कॉलबैक चलाने के लिए तैयार नहीं होते हैं, तो यह कभी-कभी अवरुद्ध हो सकता है, जो वांछित नहीं हो सकता है। मुझे नहीं लगता कि वर्तमान में ऐसा करने का एक क्लीनर तरीका है - यह उत्तर एंड्रयू स्वेतलोव द्वारा प्रदान किया गया था, जो asyncio योगदानकर्ता है; वह शायद बेहतर होगा कि कोई बेहतर विकल्प है या नहीं।:)

सामान्य तौर पर, अपने कोड, उचित लग रहा है, हालांकि मुझे लगता है कि आप इस run_once दृष्टिकोण के साथ शुरू करने के लिए उपयोग नहीं करना चाहिए। यह निर्धारित करने योग्य नहीं है, अगर आप एक लंबी सूची या एक धीमी प्रणाली थी, यह सब कुछ मुद्रित करने के लिए दो से अधिक अतिरिक्त पुनरावृत्तियों पड़ सकता है। बजाय, आप बस खत्म करने के लिए एक प्रहरी कि रिसीवर बताता है बंद करने के लिए, और उसके बाद दोनों भेजें पर प्रतीक्षा करें और coroutines प्राप्त भेजना चाहिए:

import sys 
import time 
import socket 
import asyncio 


addr = ('127.0.0.1', 1064) 
SENTINEL = b"_DONE_" 

# ... (This stuff is the same) 

@asyncio.coroutine 
def sending(addr, dataiter): 
    loop = asyncio.get_event_loop() 
    for d in dataiter: 
     print("Sending:", d) 
     sock = socket.socket() 
     yield from send_close(loop, sock, addr, str(d).encode()) 
    # Send a sentinel 
    sock = socket.socket() 
    yield from send_close(loop, sock, addr, SENTINEL) 

@asyncio.coroutine 
def receiving(addr): 
    loop = asyncio.get_event_loop() 
    sock = socket.socket() 
    try: 
     sock.setblocking(False) 
     sock.bind(addr) 
     sock.listen(5) 

     while True: 
      data = yield from accept_recv(loop, sock) 
      if data == SENTINEL: # Got a sentinel 
       return 
      print("Recevied:", data) 
    finally: sock.close() 

def main(): 
    loop = asyncio.get_event_loop() 
    # add these items to the event loop 
    recv = asyncio.async(receiving(addr), loop=loop) 
    send = asyncio.async(sending(addr, range(10)), loop=loop) 
    loop.run_until_complete(asyncio.wait([recv, send])) 

main() 

अंत में, asyncio.async सही तरीके से घटना पाश करने के लिए कार्यों को जोड़ने के लिए है । create_task अजगर 3.4.2 में जोड़ा गया है, ताकि आप किसी पुराने संस्करण है, तो यह अस्तित्व में नहीं होगा।

+0

मुझे लगता है कि मैं वास्तव में समझ है कि यह कैसे पहली जगह में काम किया के लिए घटना पाश पर "कुल नियंत्रण" होने का विचार पसंद आया। मुझे लगता है कि समझ में आता है SENTINAL हालांकि, का उपयोग पसंद :) – vitiral

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