上次对比了计算密集型,单线程居然会优于多线程,原因如下(摘自百度,哈哈哈):
Python是解释型语言,那么它在运行的时候就需要解释器了,简单描述下GIL,即global interpreter lock,全局解释器锁,就是python在运行的时候会锁定解释器,就是说在运行的时候只能是一个线程,锁死了,切换不了;每个线程在运行之前都要申请GIL,那么就必须要等上一个线程释放这把锁你才可以申请到,然后执行代码,执行完后,你再交给下一个线程,让它去执行代码,过程如下:
设置GIL -> 切换到一个线程去执行 -> 运行 -> 把线程设置为睡眠状态 -> 解锁GIL
这次对比下I/O密集型,理论上多线程肯定优于单线程,理论是不是对的呢?
对比方法,类似爬虫案例,处理30个url,单线程去挨个访问耗费时长和多线程去访问耗费时长进行比较。这里用一个基金网站为例,程序前面两个函数处理该基金网站,主要是为了获取多个url。然后单线程就直接调用函数get_url访问30个url并纪录耗时,最后用多线程对比记录耗时。结果,理论仍然是正确的哈!
# -*- encoding=UTF-8 -*-
__author__ = 'wjj1982'
__date__ = '2019/8/19 20:46'
__product__ = 'PyCharm'
__filename__ = '多线程爬某基金网'
from bs4 import BeautifulSoup as bs
import requests
import re
import time
from queue import Queue
from threading import Thread
# 输入一个url,然后get页面信息,最后返回页面content和页面code
def get_url(url):
if url is None:
return None
try:
response = requests.get(url)
except Exception as e:
print('get url failed,error is:{}'.format(e))
return None
if response.status_code != 200:
return None
return response.status_code, response.content
def handle_response(content):
bs_content = bs(content, 'html.parser', from_encoding='gb18030')
urls_fund = bs_content.find_all('ul', class_='num_right')
fund_list = []
for url_fund in urls_fund:
for each_li in url_fund.find_all('li'):
fund_info_dict = {'fund_id': '',
'fund_name': '',
'fund_url': ''}
each_a = each_li.find_all('a')
if len(each_a) > 0:
each_a = each_a[0]
fund_info_dict['fund_id'] = re.findall(r'\d+', each_a.text)[0]
fund_info_dict['fund_name'] = each_a.text.split(')')[1]
fund_info_dict['fund_url'] = each_a['href']
# fund_info_dict['fund_url'] = each_a.attrs['href']
fund_list.append(fund_info_dict)
return fund_list
# 自定义一个线程继承于threanding.Thread,用来多线程处理获取页面用,传入的时queue队列,queue队列里面放的就是多个url
# 多进程就是分别从队列里取url并行处理,最后返回code做汇总统计
class my_thread_get_url(Thread):
# 类初始化,注意这里super要init一下,否则报thread没有初始化
def __init__(self, queue):
super(my_thread_get_url, self).__init__()
self.queue = queue
def run(self):
while True:
if self.queue.empty():
break
else:
fund_url = self.queue.get()
code_response1, content_response1 = get_url(fund_url)
print(code_response1)
if __name__ == '__main__':
# 这一部分是获取所有基金的url
url = 'http://fund**************'
code_response, content_response = get_url(url)
fund_list = handle_response(content_response)
# 单线程处理30个url
print('单线程开始处理30个url')
start = time.time()
for fund in fund_list[:29]:
code_response, content_response = get_url(fund['fund_url'])
print(code_response)
end = time.time()
print('单线程处理30个url时的耗时:{}'.format(end - start))
# 多线程处理30个url
print('多线程处理30个url')
start1 = time.time()
my_queue = Queue()
my_thread_list = []
# url入列,put入列,get为出列,empty查询为空时break
for fun in fund_list[:29]:
my_queue.put(fun['fund_url'])
# 三个线程并发执行,电脑能力强的话可以更多线程执行哈,我觉得主要和CPU能力还有内存大小相关
for i in range(3):
my_thread_class = my_thread_get_url(my_queue)
my_thread_list.append(my_thread_class)
my_thread_class.start()
# 阻塞,线程阻塞等待子线程执行完,主线程结束
for t in my_thread_list:
t.join()
end1 = time.time()
print('多线程处理30个url时的耗时:{}'.format(end1 - start1))

网友评论