У меня есть Python script, который иногда отображает изображения пользователю. Изображения могут временами быть довольно большими, и они часто используются повторно. Отображение их не является критическим, но отображение связанного с ним сообщения. У меня есть функция, которая загружает требуемое изображение и сохраняет его локально. Сейчас он запускается в ряд с кодом, отображающим сообщение пользователю, но иногда это может занять более 10 секунд для нелокальных изображений. Есть ли способ, который я мог бы вызвать эту функцию, когда это было необходимо, но запустить ее в фоновом режиме, пока код продолжает выполняться? Я бы просто использовал изображение по умолчанию, пока не станет доступным.
Фоновая функция в Python
Ответ 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 переключает контексты при запуске других "зеленых". Беспокойства по поводу блокировки и т.д. Значительно сокращаются, так как одновременно работает только одна вещь, но изображение будет продолжать загружаться всякий раз, когда операция блокировки выполняется в "основном" контексте.
В зависимости от того, сколько и чего вы хотите делать в фоновом режиме, это может быть лучше или хуже, чем решения на основе потоков; конечно, это гораздо более масштабируемо (т.е. вы можете делать много других вещей в фоновом режиме), но это может не беспокоить текущую ситуацию.