Python 中的线程理解: 如何获取 `run()` 返回的已处理数据?

68 阅读2分钟

在 IBM 开发者源代码中,我阅读了关于 Python 中线程的示例。我理解了示例中的大部分内容,但有一点非常重要,那就是工作似乎都是在 run() 函数中完成的。在这个例子里,run() 函数只打印一行信息并通过队列提示任务已完成。

如果我需要在处理后返回一些数据,我应该怎么做呢?我考虑在全局变量中缓存数据,然后稍后再访问它,但这看起来不是一个正确的方法。

我们可能会想到在 self.queue.task_done() 后面紧接着向 run() 函数添加 return processed_data,以便在函数中返回已处理的数据。但是,我们无法确定在哪里捕获这个返回值,因为我们并不知道 run() 函数在哪里被调用。

#!/usr/bin/env python
import Queue
import threading
import urllib2
import time

hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com",
"http://ibm.com", "http://apple.com"]

queue = Queue.Queue()

class ThreadUrl(threading.Thread):
    """Threaded Url Grab"""
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            # 从队列中获取主机
            host = self.queue.get()

            # 获取主机的 URL 并打印页面的前 1024 字节
            url = urllib2.urlopen(host)
            print url.read(1024)

            # 向队列发出信号,表示任务已完成
            self.queue.task_done()

start = time.time()
def main():

    # 创建线程池,并向其中传递队列实例
    for i in range(5):
        t = ThreadUrl(queue)
        t.setDaemon(True)
        t.start()

    # 向队列填充数据
    for host in hosts:
        queue.put(host)

    # 等待队列,直到所有内容处理完成
    queue.join()

main()
print "Elapsed Time: %s" % (time.time() - start)

2. 解决方案

方法一:使用另一个队列来返回结果

我们不能直接从 run() 函数中返回一个值。通常情况下,每个线程中需要处理的项目不止一个,因此我们不希望在处理完一个值后就立即返回(请参阅每个线程中的 while 循环)。

我们可以使用另一个队列来返回结果:

queue = Queue.Queue()
out_queue = Queue.Queue()

class ThreadUrl(threading.Thread):
    ...
    def run(self):
        while True:
            # 从队列中获取主机
            host = self.queue.get()

            # 获取主机的 URL 并保存页面的前 1024 字节
            url = urllib2.urlopen(host)
            out_queue.put(url.read(1024))

            # 向队列发出信号,表示任务已完成
            self.queue.task_done()

...

def main():
    ...
    # 向队列填充数据   
    for host in hosts:
        queue.put(host)

    # 如果我们不想等待,则不必等到所有内容都处理完

    for _ in range(len(hosts)):
        first_1k = out_queue.get()
        print first_1k

方法二:将结果存储在同一个队列中

或者,我们可以将结果存储在同一个队列中:

class WorkItem(object):
    def __init__(self, host):
        self.host = host

class ThreadUrl(threading.Thread):
    ...
    def run(self):
        while True:
            # 从队列中获取主机
            work_item = self.queue.get()
            host = work_item.host

            # 获取主机的 URL 并保存页面的前 1024 字节
            url = urllib2.urlopen(host)
            work_item.first_1k = url.read(1024)

            # 向队列发出信号,表示任务已完成
            self.queue.task_done()

...

def main():
    ...
    # 向队列填充数据   
    work_items = [WorkItem(host) for host in hosts]
    for item in work_items:
        queue.put(item)

    # 等待队列,直到所有内容处理完成     
    queue.join()

    for item in work_items:
        print item.first_1k