【发布时间】:2015-07-19 17:40:22
【问题描述】:
所以我一直在尝试在 python 中对一些互联网连接进行多线程处理。我一直在使用多处理模块,所以我可以绕过“全局解释器锁”。但似乎系统只给python一个开放的连接端口,或者至少它只允许一次连接发生。这是我所说的一个例子。
*请注意,这是在 linux 服务器上运行的
from multiprocessing import Process, Queue
import urllib
import random
# Generate 10,000 random urls to test and put them in the queue
queue = Queue()
for each in range(10000):
rand_num = random.randint(1000,10000)
url = ('http://www.' + str(rand_num) + '.com')
queue.put(url)
# Main funtion for checking to see if generated url is active
def check(q):
while True:
try:
url = q.get(False)
try:
request = urllib.urlopen(url)
del request
print url + ' is an active url!'
except:
print url + ' is not an active url!'
except:
if q.empty():
break
# Then start all the threads (50)
for thread in range(50):
task = Process(target=check, args=(queue,))
task.start()
因此,如果您运行它,您会注意到它在函数上启动了 50 个实例,但一次只运行一个。您可能认为“全局解释器锁”正在执行此操作,但事实并非如此。尝试将函数更改为数学函数而不是网络请求,您将看到所有 50 个线程同时运行。
那么我必须使用套接字吗?或者我可以做些什么来让 python 访问更多端口?或者有什么我没有看到的?让我知道你的想法!谢谢!
*编辑
所以我编写了这个脚本来更好地测试 requests 库。好像我之前没有很好地测试过它。 (我主要用过 urllib 和 urllib2)
from multiprocessing import Process, Queue
from threading import Thread
from Queue import Queue as Q
import requests
import time
# A main timestamp
main_time = time.time()
# Generate 100 urls to test and put them in the queue
queue = Queue()
for each in range(100):
url = ('http://www.' + str(each) + '.com')
queue.put(url)
# Timer queue
time_queue = Queue()
# Main funtion for checking to see if generated url is active
def check(q, t_q): # args are queue and time_queue
while True:
try:
url = q.get(False)
# Make a timestamp
t = time.time()
try:
request = requests.head(url, timeout=5)
t = time.time() - t
t_q.put(t)
del request
except:
t = time.time() - t
t_q.put(t)
except:
break
# Then start all the threads (20)
thread_list = []
for thread in range(20):
task = Process(target=check, args=(queue, time_queue))
task.start()
thread_list.append(task)
# Join all the threads so the main process don't quit
for each in thread_list:
each.join()
main_time_end = time.time()
# Put the timerQueue into a list to get the average
time_queue_list = []
while True:
try:
time_queue_list.append(time_queue.get(False))
except:
break
# Results of the time
average_response = sum(time_queue_list) / float(len(time_queue_list))
total_time = main_time_end - main_time
line = "Multiprocessing: Average response time: %s sec. -- Total time: %s sec." % (average_response, total_time)
print line
# A main timestamp
main_time = time.time()
# Generate 100 urls to test and put them in the queue
queue = Q()
for each in range(100):
url = ('http://www.' + str(each) + '.com')
queue.put(url)
# Timer queue
time_queue = Queue()
# Main funtion for checking to see if generated url is active
def check(q, t_q): # args are queue and time_queue
while True:
try:
url = q.get(False)
# Make a timestamp
t = time.time()
try:
request = requests.head(url, timeout=5)
t = time.time() - t
t_q.put(t)
del request
except:
t = time.time() - t
t_q.put(t)
except:
break
# Then start all the threads (20)
thread_list = []
for thread in range(20):
task = Thread(target=check, args=(queue, time_queue))
task.start()
thread_list.append(task)
# Join all the threads so the main process don't quit
for each in thread_list:
each.join()
main_time_end = time.time()
# Put the timerQueue into a list to get the average
time_queue_list = []
while True:
try:
time_queue_list.append(time_queue.get(False))
except:
break
# Results of the time
average_response = sum(time_queue_list) / float(len(time_queue_list))
total_time = main_time_end - main_time
line = "Standard Threading: Average response time: %s sec. -- Total time: %s sec." % (average_response, total_time)
print line
# Do the same thing all over again but this time do each url at a time
# A main timestamp
main_time = time.time()
# Generate 100 urls and test them
timer_list = []
for each in range(100):
url = ('http://www.' + str(each) + '.com')
t = time.time()
try:
request = requests.head(url, timeout=5)
timer_list.append(time.time() - t)
except:
timer_list.append(time.time() - t)
main_time_end = time.time()
# Results of the time
average_response = sum(timer_list) / float(len(timer_list))
total_time = main_time_end - main_time
line = "Not using threads: Average response time: %s sec. -- Total time: %s sec." % (average_response, total_time)
print line
如您所见,它非常适合多线程。实际上,我的大多数测试表明 threading 模块实际上比 multiprocessing 模块快。 (我不明白为什么!)这是我的一些结果。
Multiprocessing: Average response time: 2.40511314869 sec. -- Total time: 25.6876308918 sec.
Standard Threading: Average response time: 2.2179402256 sec. -- Total time: 24.2941861153 sec.
Not using threads: Average response time: 2.1740363431 sec. -- Total time: 217.404567957 sec.
这是在我的家庭网络上完成的,我的服务器上的响应时间要快得多。我认为我的问题已经间接回答了,因为我在一个更复杂的脚本上遇到了问题。所有的建议都帮助我很好地优化了它。谢谢大家!
【问题讨论】:
-
您是否尝试过使用不同的 python 模块来完成 HTTP 工作,可能是 requests?我们知道
urllibisn't thread-safe,虽然我认为这不会影响多进程,但我会尝试使用不同的模块来找出答案。 -
你怎么知道只有一个进程在运行?我认为数学函数比 http 请求完成得快得多,虽然看起来运行是同步的,但实际上它正在执行许多请求,但由于它们很慢,所以设法清楚地写入标准输出。跨度>
-
@ReutSharabani 好吧,我一直在“htop”中检查它,但它一次只打印一个。如果它实际上正在运行多个进程,它会一次打印出许多进程。
-
这是我运行脚本时得到的:
reut@sharabani:~/python/ports$ pgrep python | wc -l输出:51 -
@ReutSharabani 是的。这正是我得到的。据我所知,这意味着已经启动了 51 个 python 线程,但这并不意味着所有 51 个线程都在运行。如果你打开“htop”,你会注意到你只有一两个线程在运行。
标签: python linux multithreading python-2.7