фонова функція в Python


88

У мене є сценарій Python, який інколи відображає зображення користувачеві. Іноді зображення можуть бути досить великими, і вони часто використовуються повторно. Відображення їх не є критичним, але відображення пов'язаного з ними повідомлення є. У мене є функція, яка завантажує потрібне зображення та зберігає його локально. Зараз він запускається вбудовано з кодом, який відображає повідомлення користувачеві, але це може зайняти більше 10 секунд для нелокальних зображень. Чи є спосіб викликати цю функцію, коли вона потрібна, але запустити її у фоновому режимі, поки код продовжує виконуватися? Я б просто використовував зображення за замовчуванням, поки не з’явиться правильне.

Відповіді:


129

Зробіть щось подібне:

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

потім в рядку, зробіть щось подібне:

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

Можливо, ви захочете перевірити, чи закінчився потік, перш ніж переходити до інших речей, зателефонувавши download_thread.isAlive()


Інтерпретатор залишається відкритим, поки потік не закриється. (приклад import threading, time; wait=lambda: time.sleep(2); t=threading.Thread(target=wait); t.start(); print('end')). Я сподівався, що "фон" мав на увазі також відокремленість.
ThorSummoner

3
Потоки @ThorSummoner містяться в одному процесі. Якщо ви хочете породити новий процес, ви захочете заглянути в модулі subprocessor multiprocessingpython.
TorelTwiddler

@TorelTwiddler Я хочу запустити функцію у фоновому режимі, але у мене є деякі обмеження ресурсів, і я не можу запустити функцію стільки разів, скільки я хочу, і хочу поставити в чергу додаткові виконання функції. Чи маєте ви уявлення про те, як мені це робити? У мене тут є своє запитання . Не могли б ви поглянути на моє запитання? Будь-яка допомога була б чудовою!
Амір

3
Якщо вам потрібні кілька параметрів: download_thread = threadading.Thread (target = function_that_downloads, args = (variable1, variable2, variableN))
georgeos

як передати кілька аргументів - як метод add(a,b)і отримати значення, що повертає цей метод
Maifee Ul Asad

7

Зазвичай спосіб зробити це - використовувати пул потоків і завантаження в чергу, які видають сигнал, який називається подією, коли це завдання закінчує обробку. Ви можете зробити це в рамках потокового модуля, який надає Python.

Для виконання зазначених дій я б використовував об’єкти подій та модуль Черги .

Однак, швидку і брудну демонстрацію того, що ви можете зробити за допомогою простої 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

Зверніть увагу, що тут немає встановленого прапора демона.


4

Я вважаю за краще використовувати 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 перемикає контексти, коли працюють інші "оранжерети". Турботи щодо блокування тощо значно зменшуються, оскільки одночасно працює лише одне, проте зображення буде продовжувати завантажуватися кожного разу, коли операція блокування виконується в "основному" контексті.

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


яка мета цього рядка from gevent import monkey; monkey.patch_all():?
nz_21

виправляє стандартну бібліотеку для сумісності gevent
gevent.org/api/gevent.monkey.html
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.