fungsi latar belakang dengan Python

89

Saya punya skrip Python yang terkadang menampilkan gambar kepada pengguna. Gambar bisa, kadang-kadang, menjadi cukup besar, dan sering digunakan kembali. Menampilkannya tidak penting, tetapi menampilkan pesan yang terkait dengannya. Saya punya fungsi yang mengunduh gambar yang dibutuhkan dan menyimpannya secara lokal. Saat ini dijalankan sesuai dengan kode yang menampilkan pesan kepada pengguna, tetapi terkadang membutuhkan waktu lebih dari 10 detik untuk gambar non-lokal. Adakah cara saya dapat memanggil fungsi ini saat diperlukan, tetapi menjalankannya di latar belakang sementara kode terus dijalankan? Saya hanya akan menggunakan gambar default sampai gambar yang benar tersedia.

Dan Hlavenka
sumber

Jawaban:

130

Lakukan sesuatu seperti ini:

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

lalu sebaris, lakukan sesuatu seperti ini:

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

Anda mungkin ingin memeriksa apakah utas telah selesai sebelum melanjutkan ke hal lain dengan menelepon download_thread.isAlive()

TorelTwiddler
sumber
Penerjemah tetap terbuka sampai utas ditutup. (contoh import threading, time; wait=lambda: time.sleep(2); t=threading.Thread(target=wait); t.start(); print('end')). Saya berharap "latar belakang" tersirat terlepas juga.
ThorSummoner
3
@ThorSummoner Threads semuanya terkandung dalam proses yang sama. Jika Anda ingin menelurkan proses baru, Anda sebaiknya melihat ke modul subprocessatau multiprocessingpython.
TorelTwiddler
@TorelTwiddler Saya ingin menjalankan suatu fungsi di latar belakang tetapi saya memiliki beberapa keterbatasan sumber daya dan tidak dapat menjalankan fungsi sebanyak yang saya inginkan dan ingin mengantri eksekusi ekstra fungsi tersebut. Apakah Anda tahu bagaimana saya harus melakukan itu? Saya punya pertanyaan saya di sini . Bisakah Anda melihat pertanyaan saya? Bantuan apa pun akan sangat bagus!
Amir
3
Jika Anda menginginkan beberapa parameter: download_thread = threading.Thread (target = function_that_downloads, args = (variable1, variable2, variableN))
georgeos
cara melewatkan beberapa argumen - seperti metode add(a,b)dan mendapatkan nilai
kembalian
7

Biasanya cara untuk melakukan ini adalah dengan menggunakan kumpulan utas dan unduhan antrian yang akan mengeluarkan sinyal, alias peristiwa, ketika tugas itu telah selesai diproses. Anda dapat melakukan ini dalam cakupan modul penguliran yang disediakan Python.

Untuk melakukan tindakan tersebut, saya akan menggunakan objek acara dan modul Antrian .

Namun, demonstrasi cepat dan kotor tentang apa yang dapat Anda lakukan dengan threading.Threadpenerapan sederhana dapat dilihat di bawah ini:

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

Mungkin masuk akal untuk tidak melakukan jajak pendapat seperti yang saya lakukan di atas. Dalam hal ini, saya akan mengubah kodenya menjadi ini:

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

Perhatikan bahwa tidak ada tanda daemon yang disetel di sini.

Mahmoud Abdelkader
sumber
4

Saya lebih suka menggunakan gevent untuk hal semacam ini:

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

Semuanya berjalan dalam satu thread, tetapi setiap kali operasi kernel memblokir, gevent mengganti konteks saat ada "greenlets" lain yang berjalan. Kekhawatiran tentang penguncian, dll. Jauh berkurang, karena hanya ada satu hal yang berjalan pada satu waktu, namun gambar akan terus diunduh setiap kali operasi pemblokiran dijalankan dalam konteks "utama".

Bergantung pada seberapa banyak, dan hal apa yang ingin Anda lakukan di latar belakang, ini bisa lebih baik atau lebih buruk daripada solusi berbasis threading; tentu saja, ini jauh lebih berskala (yaitu, Anda dapat melakukan lebih banyak hal di latar belakang), tetapi itu mungkin tidak menjadi perhatian dalam situasi saat ini.

shaunc
sumber
apa tujuan dari baris ini: from gevent import monkey; monkey.patch_all()?
nz_21
patch perpustakaan standar untuk kompatibilitas gevent
gevent.org/api/gevent.monkey.html