在 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