2015-03-18 33 views
10

Jest to podobne do Calling coroutines in asyncio.Protocol.data_received, ale myślę, że uzasadnia to nowe pytanie.Wywołanie coroutine z asyncio.Protocol.data_received

Mam prosty serwer skonfigurować jak ten

loop.create_unix_server(lambda: protocol, path=serverSocket) 

to działa dobrze, jeśli mogę to zrobić

def data_received(self, data): 
    data = b'data reply' 
    self.send(data) 

mój klient otrzyma odpowiedź. Ale nie mogę uruchomić go z jakimkolwiek połączeniem asyncio. Próbowałem wszystkich poniższych i żaden z nich nie działał.

@asyncio.coroutine 
def go(self): 
    yield from asyncio.sleep(1, result = b'data reply') 

def data_received(self, data): 
    print('Data Received', flush=True) 

    task = asyncio.get_event_loop().create_task(self.go()) 
    data = yield from asyncio.wait_for(task,10) 
    self.send(data) 

że jeden wisiał i nic drukowane (jeśli dobrze urządzone data_received z @asyncio.coroutine otrzymuję, że nie uzyskuje się z) OK, rozumiem, że za pomocą plonu w data_received nie jest w porządku.

Gdybym wypróbować nową pętlę zdarzeń, jak poniżej, który wisi w run_until_complete

loop = asyncio.new_event_loop() 
    task = loop.create_task(self.go()) 
    loop.run_until_complete(task) 
    data = task.result() 
    self.send(data) 

Jeśli używam Future, że zawiesza się również w run_until_complete

@asyncio.coroutine 
def go(self, future): 
    yield from asyncio.sleep(1) 
    future.set_result(b'data reply') 

def data_received(self, data): 
    print('Data Received', flush=True) 

    loop = asyncio.new_event_loop() 
    future = asyncio.Future(loop=loop) 
    asyncio.async(self.go(future)) 
    loop.run_until_complete(future) 
    data = future.result() 
    self.send(data) 

Poniższy zbliża, ale wraca natychmiast, a wynik jest typu asyncio.coroutines.CoroWrapper, co oznacza, że ​​linia wait_for natychmiast powróciła z nieukończonym zadaniem?

@asyncio.coroutine 
def go(self): 
    return(yield from asyncio.sleep(3, result = b'data reply')) 

@asyncio.coroutine 
def go2(self): 
    task = asyncio.get_event_loop().create_task(self.go()) 
    res = yield from asyncio.wait_for(task, 10) 
    return result 

def data_received(self, data): 
    print('Data Received', flush=True) 

    data = self.go2() 
    self.send(data) 

Trochę utknąłem naprawdę i doceniłbym pewne wskazówki na temat tego, na co patrzeć.

Odpowiedz

11

musisz dodać współprogram do pętli zdarzeń, a następnie użyj Future.add_done_callback obsłużyć wynik gdy współprogram uzupełnia:

@asyncio.coroutine 
def go(self): 
    return(yield from asyncio.sleep(3, result = b'data reply')) 

def data_received(self, data): 
    print('Data Received', flush=True) 

    task = asyncio.async(self.go()) # or asyncio.get_event_loop().create_task() 
    task.add_done_callback(self.handle_go_result) 

def handle_go_result(self, task): 
    data = task.result() 
    self.send(data) 

Wywołanie współprogram bezpośrednio data_received po prostu nie jest dozwolone, ponieważ program wywołujący nie będzie próbował tego dokonać, a tworzenie/uruchamianie nowej pętli zdarzeń wewnątrz data_received zawsze kończy się blokowaniem głównej pętli zdarzeń, dopóki wewnętrzna pętla zdarzeń nie zakończy pracy.

Po prostu chcesz zaplanować jakąś pracę z głównej pętli zdarzeń (asyncio.async/loop.create_task()) i zaplanować oddzwanianie do uruchomienia, gdy praca jest wykonywana (add_done_callback).

+0

Cześć, dziękuję za tę odpowiedź - to działa w moim kodzie. Po prostu integruję go z moim nietrywialnym kodem (który ma stos wywoławczy itp.) Myślałem o korzystaniu z callbacków, ale myślałem, że plonowanie/zadania/futures były nowszym podejściem i przynosiły korzyść, aby się nie martwić. o stosie wywołań lub jak uzyskać dane do twojego wywołania zwrotnego. Czy wiesz, dlaczego wewnętrzna pętla nie zakończyłaby swojej pracy? Albo dlaczego przyszłe podejście nie zadziałałoby z główną pętlą? – Dave

+0

Och, myślę, że wewnętrzna pętla czeka na zablokowanym zewnętrznym, aby wykonać pewne IO itd. – Dave

+1

@Dave 'asyncio' zdecydowanie preferuje podejście typu coroutine do użycia na wyższym poziomie, ale funkcje niskiego poziomu, takie jak Protokoły, nadal wykorzystują styl wywołania zwrotnego. Jeśli zamiast tego chcesz używać coroutines, [docs recommend] (https://docs.python.org/3/library/asyncio-protocol.html#coroutines-and-protocols) użyj obiektów strumieniowych zamiast protokołów. – dano