Подтвердить что ты не робот

Фоновая функция в Python

У меня есть Python script, который иногда отображает изображения пользователю. Изображения могут временами быть довольно большими, и они часто используются повторно. Отображение их не является критическим, но отображение связанного с ним сообщения. У меня есть функция, которая загружает требуемое изображение и сохраняет его локально. Сейчас он запускается в ряд с кодом, отображающим сообщение пользователю, но иногда это может занять более 10 секунд для нелокальных изображений. Есть ли способ, который я мог бы вызвать эту функцию, когда это было необходимо, но запустить ее в фоновом режиме, пока код продолжает выполняться? Я бы просто использовал изображение по умолчанию, пока не станет доступным.

4b9b3361

Ответ 1

Сделайте что-то вроде этого:

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

затем inline, сделайте что-то вроде этого:

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

Вы можете проверить, завершился ли поток, прежде чем переходить к другим вещам, вызывая download_thread.isAlive()

Ответ 2

Как правило, способ сделать это будет использовать пул потоков и загрузку очереди вверх, что приведет к выдаче сигнала, a.k.a событие, эта задача завершила обработку. Вы можете сделать это в рамках потокового модуля, который предоставляет Python.

Чтобы выполнить указанные действия, я бы использовал объекты событий и Queue модуль.

Тем не менее, краткая и грязная демонстрация того, что вы можете сделать, используя простую реализацию threading.Thread, можно увидеть ниже:

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

Возможно, было бы разумно не опросить, как я делаю выше. В этом случае я бы изменил код на это:

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

Обратите внимание, что здесь не установлен флаг демона.

Ответ 3

Я предпочитаю использовать gevent для этого:

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

Все работает в одном потоке, но всякий раз, когда блокировка ядра блокируется, gevent переключает контексты при запуске других "зеленых". Беспокойства по поводу блокировки и т.д. Значительно сокращаются, так как одновременно работает только одна вещь, но изображение будет продолжать загружаться всякий раз, когда операция блокировки выполняется в "основном" контексте.

В зависимости от того, сколько и чего вы хотите делать в фоновом режиме, это может быть лучше или хуже, чем решения на основе потоков; конечно, это гораздо более масштабируемо (т.е. вы можете делать много других вещей в фоновом режиме), но это может не беспокоить текущую ситуацию.