美文网首页
对比python的I/O密集型单线程和多线程

对比python的I/O密集型单线程和多线程

作者: 余生还长你别慌 | 来源:发表于2019-08-20 19:10 被阅读0次

上次对比了计算密集型,单线程居然会优于多线程,原因如下(摘自百度,哈哈哈):
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))

多线程处理IO密集型对比结果.jpg

相关文章

网友评论

      本文标题:对比python的I/O密集型单线程和多线程

      本文链接:https://www.haomeiwen.com/subject/qvrmsctx.html