2011-08-23 8 views
48

Tengo un script de Python que a veces muestra imágenes al usuario. Las imágenes pueden, a veces, ser bastante grandes y se reutilizan con frecuencia. Mostrarlos no es crítico, pero mostrar el mensaje asociado a ellos es. Tengo una función que descarga la imagen necesaria y la guarda localmente. Ahora mismo se ejecuta en línea con el código que muestra un mensaje para el usuario, pero a veces puede tomar más de 10 segundos para las imágenes no locales. ¿Hay alguna manera de 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 esté disponible la correcta.función de fondo en Python

+0

¿qué versión de python? –

Respuesta

70

hacer algo como esto:

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

continuación, 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=my_args) 
    download_thread.start() 
    #continue doing stuff 

es posible que desee comprobar si el mensaje ha terminado antes de pasar a otras cosas llamando download_thread.isAlive()

+0

¡Agradable y simple! Gracias. – nicky

+0

El intérprete permanece abierto hasta que se cierra el hilo. (ejemplo 'import threading, time; wait = lambda: time.sleep (2); t = threading.Thread (target = wait); t.start(); print ('end')'). Esperaba que también se desprendiera el "trasfondo". – ThorSummoner

+2

@ThorSummoner Los hilos están contenidos dentro del mismo proceso. Si está buscando generar un nuevo proceso, en su lugar querrá examinar los módulos de python 'subprocess' o' multiprocessing'. – TorelTwiddler

6

Normalmente, la forma de hacer esto sería utilizar un conjunto de subprocesos y cola de descargas, que emitirían una señal, a.k.a un evento, esa tarea ha terminado de procesarse. Puede hacer esto dentro del alcance del que proporciona Python. Para realizar dichas acciones, usaría event objects y Queue module.

Sin embargo, una demostración rápida y sucia de lo que puede hacer uso de un simple threading.Thread aplicación 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 para no sondea que estoy haciendo anteriormente. En cuyo caso, me gustaría cambiar el código para 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 

en cuenta que no hay bandera demonio establecido aquí.

3

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 hilo, pero cada vez que se bloquea una operación del kernel, gevent cambia de contexto cuando hay otros "greenlets" en ejecución. Las preocupaciones sobre el bloqueo, etc. son muy reducidas, ya que solo hay una cosa por vez, pero la imagen continuará descargando cada vez que se ejecute una operación de bloqueo en el contexto "principal".

Según la cantidad y el tipo de cosas que desee 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 podría no ser preocupante en la situación actual.