it-swarm.com.de

Hintergrundfunktion in Python

Ich habe ein Python Skript, das dem Benutzer manchmal Bilder anzeigt. Die Bilder können manchmal ziemlich groß sein und werden oft wiederverwendet. Das Anzeigen ist nicht kritisch, aber das Anzeigen des Ich habe eine Funktion, die das benötigte Bild herunterlädt und lokal speichert.Im Moment wird es inline mit dem Code ausgeführt, der dem Benutzer eine Nachricht anzeigt, aber das kann manchmal über 10 Sekunden dauern, wenn es nicht lokal ist Gibt es eine Möglichkeit, diese Funktion bei Bedarf aufzurufen, aber sie im Hintergrund auszuführen, während der Code weiter ausgeführt wird? Ich würde nur ein Standardbild verwenden, bis das richtige verfügbar ist.

68
Dan Hlavenka

Mach so etwas:

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

dann inline so etwas machen:

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

Sie können überprüfen, ob der Thread beendet ist, bevor Sie mit anderen Dingen fortfahren, indem Sie download_thread.isAlive() aufrufen.

95
TorelTwiddler

In der Regel wird dazu ein Thread-Pool verwendet und Downloads in die Warteschlange gestellt, wodurch ein Signal ausgegeben wird, und zwar ein Ereignis, bei dem die Verarbeitung dieser Task abgeschlossen ist. Sie können dies im Rahmen des Threading-Moduls Python bereitgestellt.

Um diese Aktionen auszuführen, würde ich Ereignisobjekte und Warteschlangenmodul verwenden.

Eine schnelle und schmutzige Demonstration dessen, was Sie mit einem einfachen threading.Thread Implementierung kann unten gesehen werden:

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()

Es würde wahrscheinlich Sinn machen, nicht wie oben abzufragen. In diesem Fall würde ich den Code folgendermaßen ändern:

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

Beachten Sie, dass hier kein Daemon-Flag gesetzt ist.

6

Ich bevorzuge gevent für solche Dinge:

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()

Alles läuft in einem Thread, aber wann immer eine Kerneloperation blockiert, wechselt gevent den Kontext, wenn andere "Greenlets" laufen. Die Sorgen um Sperren usw. werden erheblich reduziert, da immer nur eines ausgeführt wird. Das Image wird jedoch weiterhin heruntergeladen, wenn eine Sperroperation im "Haupt" -Kontext ausgeführt wird.

Je nachdem, wie viel und was Sie im Hintergrund tun möchten, kann dies besser oder schlechter sein als threadingbasierte Lösungen. Natürlich ist es viel skalierbarer (dh Sie können im Hintergrund viel mehr Dinge tun), aber das ist in der aktuellen Situation möglicherweise nicht von Belang.

3
shaunc