ฟังก์ชันพื้นหลังใน Python


89

ฉันมีสคริปต์ Python ที่บางครั้งแสดงภาพให้กับผู้ใช้ ในบางครั้งรูปภาพอาจมีขนาดค่อนข้างใหญ่และมักจะนำมาใช้ซ้ำ การแสดงข้อความเหล่านี้ไม่สำคัญ แต่การแสดงข้อความที่เกี่ยวข้องคือ ฉันมีฟังก์ชันที่ดาวน์โหลดรูปภาพที่ต้องการและบันทึกไว้ในเครื่อง ตอนนี้มันทำงานในแนวเดียวกับรหัสที่แสดงข้อความถึงผู้ใช้ แต่บางครั้งอาจใช้เวลานานกว่า 10 วินาทีสำหรับภาพที่ไม่ใช่ในเครื่อง มีวิธีใดบ้างที่ฉันสามารถเรียกใช้ฟังก์ชันนี้เมื่อจำเป็น แต่เรียกใช้ในพื้นหลังในขณะที่โค้ดยังคงทำงานอยู่ ฉันจะใช้ภาพเริ่มต้นจนกว่าภาพที่ถูกต้องจะพร้อมใช้งาน

คำตอบ:


130

ทำสิ่งนี้:

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 เธรดทั้งหมดอยู่ในกระบวนการเดียวกัน หากคุณต้องการสร้างกระบวนการใหม่คุณจะต้องดูโมดูลsubprocessหรือmultiprocessingpython แทน
TorelTwiddler

@TorelTwiddler ฉันต้องการเรียกใช้ฟังก์ชันในพื้นหลัง แต่ฉันมีข้อ จำกัด ด้านทรัพยากรและไม่สามารถเรียกใช้ฟังก์ชันได้หลายครั้งตามที่ฉันต้องการและต้องการจัดคิวการดำเนินการเพิ่มเติมของฟังก์ชัน คุณมีความคิดว่าฉันควรทำอย่างไร? ฉันมีคำถามของฉันที่นี่ คุณช่วยดูคำถามของฉันได้ไหม ความช่วยเหลือใด ๆ จะดีมาก!
Amir

3
หากคุณต้องการพารามิเตอร์หลายตัว: download_thread = threading.Thread (target = function_that_downloads, args = (variable1, variable2, variableN))
georgeos

วิธีการส่งหลาย args - เช่นเดียวกับวิธีการ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

สังเกตว่าไม่มีการตั้งค่าสถานะ daemon ที่นี่


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.