función de fondo en Python

Tengo un script de Python que a veces muestra imágenes al usuario. A veces, las imágenes pueden ser bastante grandes y se reutilizan a menudo. Mostrarlos no es crítico, pero mostrar el mensaje asociado con ellos es. Tengo una función que descarga la imagen necesaria y la guarda localmente. En este momento se ejecuta en línea con el código que muestra un mensaje al usuario, pero a veces puede tomar más de 10 segundos para las imágenes no locales. ¿Hay alguna forma de que pueda llamar a esta función cuando sea necesaria, pero ejecutarla en segundo plano mientras el código continúa ejecutándose? Solo usaría una imagen predeterminada hasta que la correcta esté disponible.

Haz algo como esto:

def function_that_downloads(my_args): # do some long download here 

luego en línea, hacer algo como esto:

 import threading def my_inline_function(some_args): # do some stuff download_thread = threading.Thread(target=function_that_downloads, args=some_args) download_thread.start() # continue doing stuff 

Es posible que desee comprobar si el hilo ha finalizado antes de pasar a otras cosas llamando a download_thread.isAlive()

Normalmente, la forma de hacer esto sería utilizar un grupo de subprocesos y poner en cola las descargas, lo que emitiría una señal, también conocida como evento, que la tarea ha finalizado su procesamiento. Puede hacer esto dentro del scope del módulo de subprocesamiento que proporciona Python.

Para realizar dichas acciones, usaría objetos de evento y el módulo de Cola .

Sin embargo, una demostración rápida y sucia de lo que puede hacer usando un threading.Thread simple. La implementación de threading.Thread puede verse a continuación:

 import os import threading import time import urllib2 class ImageDownloader(threading.Thread): def __init__(self, function_that_downloads): threading.Thread.__init__(self) self.runnable = function_that_downloads self.daemon = True def run(self): self.runnable() def downloads(): with open('somefile.html', 'w+') as f: try: f.write(urllib2.urlopen('http://google.com').read()) except urllib2.HTTPError: f.write('sorry no dice') print 'hi there user' print 'how are you today?' thread = ImageDownloader(downloads) thread.start() while not os.path.exists('somefile.html'): print 'i am executing but the thread has started to download' time.sleep(1) print 'look ma, thread is not alive: ', thread.is_alive() 

Probablemente tendría sentido no votar como lo estoy haciendo arriba. En cuyo caso, cambiaría el código a esto:

 import os import threading import time import urllib2 class ImageDownloader(threading.Thread): def __init__(self, function_that_downloads): threading.Thread.__init__(self) self.runnable = function_that_downloads def run(self): self.runnable() def downloads(): with open('somefile.html', 'w+') as f: try: f.write(urllib2.urlopen('http://google.com').read()) except urllib2.HTTPError: f.write('sorry no dice') print 'hi there user' print 'how are you today?' thread = ImageDownloader(downloads) thread.start() # show message thread.join() # display image 

Observe que no hay ninguna bandera de demonio establecida aquí.

Prefiero usar gevent para este tipo de cosas:

 import gevent from gevent import monkey; monkey.patch_all() greenlet = gevent.spawn( function_to_download_image ) display_message() # ... perhaps interaction with the user here # this will wait for the operation to complete (optional) greenlet.join() # alternatively if the image display is no longer important, this will abort it: #greenlet.kill() 

Todo se ejecuta en un solo subproceso, pero cada vez que una operación del kernel se bloquea, gevent cambia de contexto cuando hay otros “greenlets” en ejecución. Las preocupaciones sobre el locking, etc. se reducen mucho, ya que solo se ejecuta una cosa a la vez, pero la imagen continuará descargándose cada vez que se ejecute una operación de locking en el contexto “principal”.

Dependiendo de cuánto y qué tipo de cosas quiera hacer en segundo plano, esto puede ser mejor o peor que las soluciones basadas en subprocesos; sin duda, es mucho más escalable (es decir, puede hacer muchas más cosas en segundo plano), pero eso no es motivo de preocupación en la situación actual.