Python高手进阶|实战4大并发秘籍

要点:

  • 手动线程池
  • concurrent.futures线程池
  • concurrent.futures进程池
  • gevent协程

1、实战爬取维基百科例子

平时咱们有不少任务,尤为是比较耗时的大量任务要处理,必定会用到并发处理。毕竟串行太慢了,下面咱们去爬一个维基百科的网站:python

输入图片说明

咱们来爬取红框里面的导航文本部分,这是一个很是简单的爬虫(关于爬虫的文章前面写的太多太多了,你们能够翻历史文章)
1).链接网页

输入图片说明

2).爬取网页

输入图片说明

  • 函数设计的时候咱们但愿入参是一个元组(url,words),方便后面作并发处理
  • 网页很是简单,直接用requests取爬取,获取text
  • 用pyquery来解析网页,获取对国家的描述
  • 数据结构用字典对来存储

2、PK前作点准备工做

1).若是咱们如今要爬取100个国家的信息,有几种办法呢:
  • 最慢的串行爬取
  • 本身手动构建一个线程池,把要爬取的100国家都扔到共享队列里面,让多个线程共享爬取
  • 利用concurrent.futures标准库里的线程池来爬去
  • 用多进程来爬取,虽然网页请求是CPU密集型的,用进程有点浪费,可是咱们做为对比,是能够试一下的
  • 用协程也叫微线程,是一种绿色线程,用来作高并发很爽
2).为了准确的计算每一种方法的耗时,咱们写一个函数专门来计算时间:

输入图片说明

下面咱们用上面的5种方法逐一运行,为了简单期间咱们统一爬取5个国家,每种方法上面用装饰器@cost_time来计算一下,看看到底哪一种方便比较简单,速度又最快~~

3、慢慢的串行处理

先来段最通俗的one by one的串行处理

输入图片说明

>>>

输入图片说明

爬取5个国家的简介花了17秒,串行由于在等待服务器的相应的时候傻等,因此浪费了不少时间

4、手动建多线程共享队列

利用queue有锁的功能,手动把数据塞进队列,而后多个线程共享爬取

输入图片说明

>>>

输入图片说明

多线程确实很是快,只须要5秒左右就搞定了,可是用这种方法代码太多,有没有更优美的方法呢

5、用标准库里面的线程池

与其动手造轮子,不如用无所不能的库,Python里面的库真的太多太多了!这也是Python为啥这么火爆的缘由之一.

输入图片说明

>>>

输入图片说明

发现用系统的线程池跟手动的几乎差很少,可是你们发现没有用轮子来处理,代码量很是小,并且很优美!(这是Python之美,能用轮子尽可能用轮子,简洁高效).

有同窗会问,有没有什么状况是必定要手动构建线程池,而不能用 <ThreadPoolExecutor >,确实有这样的状况,你们思考一下,不明白的留言给我,偷偷告诉你.

6、用标准库的里进程池

既然上面有线程池,必定有进程池吧。是的,咱们下面来看看杀鸡用牛刀的多进程处理,须要几秒呢:

输入图片说明

>>>

输入图片说明

差很少也是5-6秒左右,多进程仍是比较快的!可是咱们这里是5个国家,若是500个你不可能开100个进程来处理呢,若是碰到很是巨大的并发量,又要节省系统资源,又要速度很快,怎么办呢,咱们看最后一招

7、绝招,用协程来并发

用一下gevent这个库,功能强大使用简单,对协程的封装比较好。每当一个协程阻塞时,程序将自动调度,gevent帮咱们处理了全部的底层细节

输入图片说明

gevent.spawn来建立一个一个的协程对象,而后joinall会等待全部的协程运行完成,最后咱们就获取到几个国家的简介数据。
>>>

输入图片说明

哇协程果真很牛逼,只须要2.8秒左右,很是舒爽的感受!可是这里由于requests库有个缺点,访问的时候是上一个访问结束,才能进行下一次访问!因此须要用gevent的猴子补丁.另外gevent虽然很好,可是它是大规模并发,若是发起10000个网络请求,估计很快会被封IP!


好咱们来总结一下:

  • 同步处理:17秒
  • 异步手动建线程池:5.5秒
  • 异步标准库线程池:5.2秒
  • 异步标准库进程池:5.9秒
  • 并发协程:2.8秒
很明显用Gevent最快,尤为是在大规模的几十万级别的并发处理效果很是明显.
线程池也是一个不错的选择,并且比较灵活,若是须要多个并发任务之间有交互的话,仍是须要用线程池.
那进程池呢,咱们上面考虑的都是IO 密集型task,若是咱们碰到了CPO型的那就必需要多核多CPU运行才能加速。毕竟Python有一个烦人的GIL,

好今天的文章就写到这里,欢迎留言讨论服务器

文章摘自:菜鸟学python微信微信