2015-11-03 5 views
8

codice Qui è semplificata, che utilizza python3 coroutine e imposta gestore per i segnali siging e SIGTERM per l'arresto lavoro correttamente:python3 asyncio "Task è stato distrutto, ma è in attesa" con qualche specifica condizione

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 
import argparse 
import asyncio 
import signal 
import sys 


def my_handler(signum, frame): 
    print('Stopping') 
    asyncio.get_event_loop().stop() 
    # Do some staff 
    sys.exit() 


@asyncio.coroutine 
def prob_ip(ip_addr): 

    print('Ping ip:%s' % ip_addr) 
    proc = yield from asyncio.create_subprocess_exec('ping', '-c', '3', ip_addr) 
    ret_code = yield from proc.wait() 
    if ret_code != 0: 
     print("ip:%s doesn't responding" % ip_addr) 
     # Do some staff 
     yield from asyncio.sleep(2) 
     # Do more staff 
     yield from asyncio.sleep(16) 


@asyncio.coroutine 
def run_probing(): 

    print('Start probing') 
    # Do some staff 
    yield from asyncio.sleep(1) 

    while True: 
     yield from asyncio.wait([prob_ip('192.168.1.3'), prob_ip('192.168.1.2')]) 
     yield from asyncio.sleep(60) 


def main(): 
    parser = argparse.ArgumentParser() 
    parser.description = "Probing ip." 
    parser.parse_args() 

    signal.signal(signal.SIGINT, my_handler) 
    signal.signal(signal.SIGTERM, my_handler) 

    asyncio.get_event_loop().run_until_complete(run_probing()) 


if __name__ == '__main__': 
    main() 

quando corro it via:

python3 test1.py 

Si ferma con Ctrl-C senza alcun avviso. Ma quando l'eseguo via:

python3 -m test1 

Esso stampa avvertimento Ctrl-C:

$ python3 -m test1 
Start probing 
Ping ip:192.168.1.2 
Ping ip:192.168.1.3 
PING 192.168.1.2 (192.168.1.2): 56 data bytes 
PING 192.168.1.3 (192.168.1.3): 56 data bytes 
^C--- 192.168.1.2 ping statistics --- 
--- 192.168.1.3 ping statistics --- 
1 packets transmitted, 0 packets received, 100% packet loss 
1 packets transmitted, 0 packets received, 100% packet loss 
Stopping 
Task was destroyed but it is pending! 
task: <Task pending coro=<prob_ip() running at /tmp/test1.py:22> wait_for=<Future pending cb=[Task._wakeup()]> cb=[_wait.<locals>._on_completion() at /usr/lib/python3.4/asyncio/tasks.py:394]> 
Task was destroyed but it is pending! 
task: <Task pending coro=<prob_ip() running at /tmp/test1.py:22> wait_for=<Future pending cb=[Task._wakeup()]> cb=[_wait.<locals>._on_completion() at /usr/lib/python3.4/asyncio/tasks.py:394]> 

stesso avvertimento ricevo se installo questo script tramite:

from setuptools import setup 

setup(name='some_scripts', 
     version='1.0.0.0', 
     author='Some Team', 
     author_email='[email protected]', 
     url='https://www.todo.ru', 
     description='Some scripts', 
     packages=['my_package'], 
     entry_points={'console_scripts': [ 
      'test1=my_package.test1:main', 
     ]}, 
    ) 

mio pitone la versione è "3.4.2"

+1

È possibile utilizzare [loop.add_signal_handler] (https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.BaseEventLoop.add_signal_handler), sebbene non sia correlato al problema. – Vincent

+0

Sì, grazie, in questo modo sembra migliore nel mio caso. Ma non aiuta. Ho ancora gli stessi avvertimenti. – willir

risposta

8

Ok. Penso di aver capito come devo interrompere tutti i compiti.

  1. Prima di tutto, per quanto ho capito. BaseEventLoop.stop() si ferma solo per BaseEventLoop.run_forever(). Quindi si dovrebbero cancellare tutte le attività tramite Future.cancel. Per ottenere tutte le attività è possibile utilizzare il metodo statico Task.all_tasks.
  2. Dopo l'annullamento di tutte le attività, l'eccezione asyncio.CancelledError verrà sollevata da run_until_complete. Quindi si dovrebbe prenderlo, se non si vuole stamparlo su stderr.
  3. E inoltre, in alcuni casi, viene visualizzato questo errore: TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object. Ho trovato alcuni argomenti su questo errore:

    Tutti dicono che può essere risolto con la chiusura ad anello prima di uscire di applicazione.

in modo da ottenere questo codice:

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 
import asyncio 
import signal 


def my_handler(): 
    print('Stopping') 
    for task in asyncio.Task.all_tasks(): 
     task.cancel() 


@asyncio.coroutine 
def do_some(some_args): 
    while True: 
     print("Do staff with %s" % some_args) 
     yield from asyncio.sleep(2) 


def main(): 
    loop = asyncio.get_event_loop() 

    loop.add_signal_handler(signal.SIGINT, my_handler) 

    try: 
     loop.run_until_complete(asyncio.wait([do_some(1), do_some(2)])) 
    except asyncio.CancelledError: 
     print('Tasks has been canceled') 
    finally: 
     loop.close() 


if __name__ == '__main__': 
    main() 

Funziona anche con signal.signal. Ma come Vincent noticedloop.add_signal_handler sembra meglio in questo caso.

Ma non sono ancora sicuro che questo sia il modo migliore per interrompere tutte le attività.

+1

(1) a differenza dei futures, le attività possono ignorare '.cancel()' (catch 'CancelledError') (2) se le attività devono essere pulite allora è possibile ['loop.run_until_complete (asyncio.gather (* asyncio.Task.all_tasks())) 'before' loop.close() '] (http://stackoverflow.com/q/27796294/4279) – jfs

+0

Se non si desidera che il gestore di segnale cancelli tutte le attività in esecuzione, dare un'occhiata a la mia risposta (modificata). – Vincent

+0

@ J.F.Sebastian Sì, grazie, devo aspettare le attività non completate prima di uscire. Ma (non so perché) asyncio.gather non funziona in questo caso (alla lista in python3.5.0). asyncio.wait funziona, quindi otteniamo tale codice: https://gist.github.com/willir/b521450b66be6e6b238c. Modifica: Anche io non so perché non ho bisogno di catturare asyncio.CancelledError da questo (in realtà il secondo) loop.run_until_complete. – willir

2

Utilizzare asyncio.gather anziché asyncio.wait :

Cancellation: if the outer Future is cancelled, all children (that have not completed yet) are also cancelled.

Esempio:

def handler(future, loop): 
    future.cancel() 
    loop.stop() 

@asyncio.coroutine 
def do_some(arg): 
    while True: 
     print("Do stuff with %s" % arg) 
     yield from asyncio.sleep(2) 

loop = asyncio.get_event_loop() 
future = asyncio.gather(do_some(1), do_some(2)) 
loop.add_signal_handler(signal.SIGINT, handler, future, loop) 
loop.run_forever() 
Problemi correlati