现在我正在研究如何尽快从网站上获取数据。为了获得更快的速度,我考虑使用多线程。这是我用来测试多线程和简单帖子之间差异的代码。
import threading
import time
import urllib
import urllib2
class Post:
def __init__(self, website, data, mode):
self.website = website
self.data = data
#mode is either "Simple"(Simple POST) or "Multiple"(Multi-thread POST)
self.mode = mode
def post(self):
#post data
req = urllib2.Request(self.website)
open_url = urllib2.urlopen(req, self.data)
if self.mode == "Multiple":
time.sleep(0.001)
#read HTMLData
HTMLData = open_url.read()
print "OK"
if __name__ == "__main__":
current_post = Post("http://forum.xda-developers.com/login.php", "vb_login_username=test&vb_login_password&securitytoken=guest&do=login", \
"Simple")
#save the time before post data
origin_time = time.time()
if(current_post.mode == "Multiple"):
#multithreading POST
for i in range(0, 10):
thread = threading.Thread(target = current_post.post)
thread.start()
thread.join()
#calculate the time interval
time_interval = time.time() - origin_time
print time_interval
if(current_post.mode == "Simple"):
#simple POST
for i in range(0, 10):
current_post.post()
#calculate the time interval
time_interval = time.time() - origin_time
print time_interval
正如您所看到的,这是一个非常简单的代码。首先我将模式设置为“简单”,我可以得到时间间隔: 50s (也许我的速度有点慢:()。然后我将模式设置为“多个”,然后我得到时间间隔: 35 。从中可以看出,多线程实际上可以提高速度,但结果并不像我想象的那么好。我想获得更快的速度。
从调试开始,我发现该程序主要阻塞在open_url = urllib2.urlopen(req, self.data)
行,这行代码需要花费大量时间来发布和接收来自指定网站的数据。我想也许我可以通过添加time.sleep()
并在urlopen
函数中使用多线程来获得更快的速度,但我不能这样做,因为它是python自己的函数。
如果不考虑服务器阻止后期速度的可能限制,我还能做些什么才能获得更快的速度?或者我可以修改的任何其他代码?很多!
答案 0 :(得分:9)
在许多情况下,python的线程并不能很好地提高执行速度......有时,它会使情况变得更糟。有关详细信息,请参阅David Beazley's PyCon2010 presentation on the Global Interpreter Lock / Pycon2010 GIL slides。这个演示文稿非常有用,我强烈推荐给任何考虑线程的人......
尽管David Beazley的演讲解释了网络流量改进了Python线程模块的调度,但您应该使用multiprocessing module。我在你的代码中包含了这个选项(参见我的答案的底部)。
在我的一台旧机器上运行它(Python 2.6.6):
current_post.mode == "Process" (multiprocessing) --> 0.2609 seconds
current_post.mode == "Multiple" (threading) --> 0.3947 seconds
current_post.mode == "Simple" (serial execution) --> 1.650 seconds
我同意TokenMacGuy的评论,上面的数字包括将.join()
移到另一个循环。如您所见,python的多处理速度明显快于线程化。
from multiprocessing import Process
import threading
import time
import urllib
import urllib2
class Post:
def __init__(self, website, data, mode):
self.website = website
self.data = data
#mode is either:
# "Simple" (Simple POST)
# "Multiple" (Multi-thread POST)
# "Process" (Multiprocessing)
self.mode = mode
self.run_job()
def post(self):
#post data
req = urllib2.Request(self.website)
open_url = urllib2.urlopen(req, self.data)
if self.mode == "Multiple":
time.sleep(0.001)
#read HTMLData
HTMLData = open_url.read()
#print "OK"
def run_job(self):
"""This was refactored from the OP's code"""
origin_time = time.time()
if(self.mode == "Multiple"):
#multithreading POST
threads = list()
for i in range(0, 10):
thread = threading.Thread(target = self.post)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
#calculate the time interval
time_interval = time.time() - origin_time
print "mode - {0}: {1}".format(method, time_interval)
if(self.mode == "Process"):
#multiprocessing POST
processes = list()
for i in range(0, 10):
process = Process(target=self.post)
process.start()
processes.append(process)
for process in processes:
process.join()
#calculate the time interval
time_interval = time.time() - origin_time
print "mode - {0}: {1}".format(method, time_interval)
if(self.mode == "Simple"):
#simple POST
for i in range(0, 10):
self.post()
#calculate the time interval
time_interval = time.time() - origin_time
print "mode - {0}: {1}".format(method, time_interval)
return time_interval
if __name__ == "__main__":
for method in ["Process", "Multiple", "Simple"]:
Post("http://forum.xda-developers.com/login.php",
"vb_login_username=test&vb_login_password&securitytoken=guest&do=login",
method
)
答案 1 :(得分:7)
你做错的最重要的事情就是给你thread.start()
和thread.join()
打电话的方式,这会影响你的吞吐量:
for i in range(0, 10):
thread = threading.Thread(target = current_post.post)
thread.start()
thread.join()
每次循环,你创建一个线程,启动它,然后等待它完成继续前进到下一个线程。你根本没有做任何事情!
你应该做的是:
threads = []
# start all of the threads
for i in range(0, 10):
thread = threading.Thread(target = current_post.post)
thread.start()
threads.append(thread)
# now wait for them all to finish
for thread in threads:
thread.join()
答案 2 :(得分:1)
请记住,多线程可以在Python中“提高速度”的唯一情况是,您的操作就像这个严重受I / O限制。否则多线程不会增加“速度”,因为它不能在多个CPU上运行(不,即使你有多个内核,python也不会那样工作)。当你想要同时完成两件事情时,你应该使用多线程,而不是当你想要两件事并行时(即两个进程分别运行)。
现在,你实际做的事情实际上不会提高任何单一DNS查询的速度,但它会允许在等待其他一些查询的结果时拍摄多个请求,但是你应该小心如何你做的很多,或者你只会使响应时间比现在更糟。
另请停止使用urllib2,并使用请求:http://docs.python-requests.org
答案 3 :(得分:0)
DNS查找需要时间。你无能为力。大延迟是首先使用多个线程的一个原因 - 多个查找广告站点GET / POST可以并行发生。
转移睡眠() - 它没有帮助。