Python中的后台功能


88

我有一个Python脚本,有时会向用户显示图像。这些图像有时可能很大,并且经常重复使用。显示它们并不重要,但是显示与它们关联的消息是至关重要的。我有一个功能,可以下载所需的图像并将其保存在本地。现在,它与向用户显示消息的代码内联运行,但是对于非本地图像,有时可能需要10秒钟以上。有没有一种方法可以在需要时调用此函数,但是在代码继续执行的同时在后台运行?我只会使用默认图像,直到正确的图像可用为止。

Answers:


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线程全部包含在同一进程中。如果您希望产生一个新的进程,则需要查看subprocessmultiprocessingpython模块。
TorelTwiddler

@TorelTwiddler我想在后台运行一个函数,但是我有一些资源限制,无法多次运行该函数,并且希望将该函数的额外执行排队。您对我该怎么做有任何想法吗?我在这里有我的问题。你能看看我的问题吗?任何帮助将是巨大的!
阿米尔(Amir)

3
如果您需要多个参数:download_thread = threading.Thread(target = function_that_downloads,args =(variable1,variable2,variableN))
georgeos

如何传递多个参数-就像方法一样add(a,b)并获取该方法的返回值
Maifee Ul Asad

7

通常,执行此操作的方法是使用线程池和队列下载,当任务完成处理时,它们将发出信号(又称为事件)。您可以在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

注意,这里没有设置守护程序标志。


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就会在运行其他“ greenlet”时切换上下文。由于一次只运行一件事,因此减少了对锁定等的担忧,但是只要在“主要”上下文中执行阻止操作,映像就将继续下载。

取决于您要在后台执行多少操作以及执行哪种操作,这可能比基于线程的解决方案更好或更糟。当然,它具有更大的可伸缩性(即,您可以在后台执行更多操作),但是在当前情况下这可能并不重要。


这行的目的是from gevent import monkey; monkey.patch_all()什么?
nz_21,

补丁GEVENT兼容性标准库- gevent.org/api/gevent.monkey.html
shaunc
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.