2016-05-18 1 views
1

J'ai une application web2py qui sert essentiellement d'interface de navigation pour un script Python. Ce script revient généralement assez rapidement, mais peut parfois prendre beaucoup de temps. Je veux fournir un moyen pour l'utilisateur d'arrêter l'exécution du script si cela prend trop de temps.Arrêt d'une action de longue durée dans web2py avec multiprocessing

J'appelle actuellement la fonction comme ceci:

def myView(): # this function is called from ajax 
    session.model = myFunc() # myFunc is from a module which i have complete control over 
    return dict(model=session.model) 

myFunc, lorsqu'elle est appelée avec certaines options, utilise multitraitement mais finit par prendre beaucoup de temps. J'ai besoin d'un moyen de terminer la fonction, ou à tout le moins les enfants du fil.

La première chose que j'ai essayé était de courir myFunc dans un nouveau processus, et rouler mon propre système d'événement simple pour le tuer:

# in the controller 
def myView(): 
    p_conn, c_conn = multiprocessing.Pipe() 
    events = multiprocessing.Manager().dict() 
    proc = multiprocessing.Process(target=_fit, args=(options, events c_conn)) 
    proc.start() 
    sleep(0.01) 
    session.events = events 
    proc.join() 
    session.model = p_conn.recv() 
    return dict(model=session.model) 

def _fit(options, events pipe): 
    pipe.send(fitting.logistic_fit(options=options, events=events)) 
    pipe.close() 

def stop(): 
    try: 
     session.events['kill']() 
    except SystemExit: 
     pass # because it raises that error intentionally 
    return dict() 

# in the module 
def kill(): 
    print multiprocessing.active_children() 
    for p in multiprocessing.active_children(): 
     p.terminate() 
    raise SystemExit 

def myFunc(options, events): 
    events['kill'] = kill 

je suis tombé sur quelques problèmes majeurs avec cela.

  1. La session stop() n'a pas toujours la même que la session myView(), donc session.events était pas.
  2. Même lorsque la session était la même, kill() ne supprimait pas correctement les enfants.
  3. La fonction à exécution longue bloquerait le thread web2py, donc stop() n'a même pas été traitée jusqu'à la fin de la fonction.

Je considérais pas join() et appellent en utilisant AJAX pour récupérer le résultat de la fonction à un moment plus tard, mais je n'ai pas pu enregistrer l'objet de processus session pour une utilisation ultérieure. Le tuyau semblait pouvoir être décapé, mais j'ai ensuite eu le problème de ne pas pouvoir accéder à la même session à partir d'une autre vue.

Comment puis-je implémenter cette fonctionnalité?

Répondre

1

Pour les tâches longues, il est préférable de les mettre en file d'attente via le scheduler intégré. Si vous souhaitez autoriser l'utilisateur à arrêter manuellement une tâche qui prend trop de temps, vous pouvez utiliser la méthode scheduler.stop_task(ref) (où ref correspond à la tâche id ou uuid). Alternativement, lorsque vous mettez en file d'attente une tâche, vous pouvez spécifier un délai d'attente, de sorte qu'il s'arrêtera automatiquement s'il n'est pas terminé dans le délai imparti.

Vous pouvez effectuer une simple interrogation Ajax pour notifier le client lorsque la tâche est terminée (ou implémenter quelque chose de plus sophistiqué avec les Websockets ou SSE).

+0

Pouvez-vous développer votre réponse? Quelques choses spécifiques qui mériteraient d'être répondues: Le planificateur démarre-t-il la tâche immédiatement, à partir d'un script? Comment obtenir le résultat de la tâche? Comment forcer-arrêter la tâche? Tel qu'il est, cela ne répond pas complètement à la question. – Scimonster

+0

J'ai mis à jour la réponse pour répondre à votre dernière question. Vos deux premières questions sont abordées dans la documentation liée. Comme ils impliquent la fonctionnalité générale du planificateur et ne font pas partie de votre question initiale, je ne mettrai pas à jour la réponse, mais je mentionnerai brièvement que les nouvelles tâches sont immédiatement mises en file d'attente immédiatement (vous pouvez forcer un ouvrier à vérifier immédiatement la file d'attente 'immediate = True'), et la méthode' scheduler.task_status() 'est utilisée pour récupérer les résultats. – Anthony