Python中的后台函数

Dan*_*nka 74 python multithreading

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

Tor*_*ler 106

做这样的事情:

def function_that_downloads(my_args):
    # do some long download here
Run Code Online (Sandbox Code Playgroud)

然后内联,做这样的事情:

import threading
def my_inline_function(some_args):
    # do some stuff
    download_thread = threading.Thread(target=function_that_downloads, args=some_args)
    download_thread.start()
    # continue doing stuff
Run Code Online (Sandbox Code Playgroud)

您可能想要检查线程是否已完成,然后再通过调用继续执行其他操作 download_thread.isAlive()

  • 如果需要多个参数:download_thread = threading.Thread(target=function_that_downloads, args=(variable1, variable2, variableN)) (3认同)
  • @ThorSummoner线程都包含在同一个进程中.如果您想要生成一个新进程,您将需要查看`subprocess`或`multiprocessing` python模块. (2认同)

Mah*_*der 6

通常,执行此操作的方法是使用线程池并排队下载,这将发出任务已完成处理的信号(即事件).您可以在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()
Run Code Online (Sandbox Code Playgroud)

不像我上面那样进行民意调查可能是有意义的.在这种情况下,我会将代码更改为:

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
Run Code Online (Sandbox Code Playgroud)

请注意,此处未设置守护程序标志.


sha*_*unc 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()
Run Code Online (Sandbox Code Playgroud)

一切都在一个线程中运行,但每当内核操作阻塞时,gevent 就会在有其他“greenlet”运行时切换上下文。关于锁定等的担忧大大减少,因为一次只有一件事在运行,但只要在“主”上下文中执行阻塞操作,图像就会继续下载。

根据您想要在后台执行的操作量和类型,这可能比基于线程的解决方案更好或更差;当然,它更具可扩展性(即您可以在后台做更多事情),但在当前情况下这可能不是问题。