Python中的后台函数
我有一个Python脚本,有时候会给用户显示图片。这些图片有时候比较大,而且经常会被重复使用。显示这些图片并不是特别重要,但与它们相关的信息却很重要。我有一个函数可以下载需要的图片并保存在本地。现在这个函数和显示信息的代码是一起运行的,但对于非本地的图片,有时候需要超过10秒才能显示。有没有办法让我在需要的时候调用这个函数,但让它在后台运行,同时代码可以继续执行呢?我可以先用一张默认的图片,等到正确的图片下载完成后再显示。
4 个回答
6
我更喜欢用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会在其他“绿色小任务”运行时切换上下文。这样就减少了对锁等问题的担忧,因为同一时间只有一件事情在运行,而当“主”上下文中有阻塞操作时,图片仍然会继续下载。
根据你想在后台做多少事情以及做什么事情,这种方法可能比基于线程的解决方案要好,也可能更差;当然,它在扩展性上要好得多(也就是说,你可以在后台同时做很多事情),但在当前情况下,这可能不是一个重要的问题。
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
注意,这里没有设置守护线程标志。
175
可以这样做:
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()
来查看。