Python多进程、多线程、协程

时间:2023-02-22 12:27:29

转载:https://www.cnblogs.com/huangguifeng/p/7632799.html

首先我们来了解下python中的进程,线程以及协程!

从计算机硬件角度:

计算机的核心是CPU,承担了所有的计算任务。

一个CPU,在一个时间切片里只能运行一个程序。

从操作系统的角度:

进程和线程,都是一种CPU的执行单元。

进程:表示一个程序的上下文执行活动(打开、执行、保存...)

线程:进程执行程序时候的最小调度单位(执行a,执行b...)

一个程序至少有一个进程,一个进程至少有一个线程。

并行 和 并发:

并行:多个CPU核心,不同的程序就分配给不同的CPU来运行。可以让多个程序同时执行。

cpu1 -------------

cpu2 -------------

cpu3 -------------

cpu4 -------------

并发:单个CPU核心,在一个时间切片里一次只能运行一个程序,如果需要运行多个程序,则串行执行。

cpu1  ----  ----

cpu1    ----  ----

多进程/多线程:

表示可以同时执行多个任务,进程和线程的调度是由操作系统自动完成。

进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。

进程之间的通信有操作系统传递,导致通讯效率低,切换开销大。

线程:一个进程可以有多个线程,所有线程共享进程的内存空间,通讯效率高,切换开销小。

共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁"。

一个线程在访问内存空间的时候,其他线程不允许访问,必须等待之前的线程访问结束,才能使用这个内存空间。

互斥锁:一种安全有序的让多个线程访问内存空间的机制。

Python的多线程:

GIL 全局解释器锁:线程的执行权限,在Python的进程里只有一个GIL。

一个线程需要执行任务,必须获取GIL。

好处:直接杜绝了多个线程访问内存空间的安全问题。

坏处:Python的多线程不是真正多线程,不能充分利用多核CPU的资源。

但是,在I/O阻塞的时候,解释器会释放GIL。

所以:

多进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。 multiprocessing

缺陷:多个进程之间通信成本高,切换开销大。

多线程:密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。

threading.Thread、multiprocessing.dummy

缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。

协程:又称微线程,在单线程上执行多个任务,用函数切换,开销极小。不通过操作系统调度,没有进程、线程的切换开销。genvent,monkey.patchall

多线程请求返回是无序的,那个线程有数据返回就处理那个线程,而协程返回的数据是有序的。

缺陷:单线程执行,处理密集CPU和本地磁盘IO的时候,性能较低。处理网络I/O性能还是比较高.

下面以这个网站为例,采用三种方式爬取。爬取前250名的电影。。

https://movie.douban.com/top250?start=0

通过分析网页发现第2页的url start=25,第3页的url start=50,第3页的start=75。因此可以得出这个网站每一页的数局是通过递增start这个参数获取的。

一般不看第一页的数据,第一页的没有参考价值。

Python多进程、多线程、协程

这次我们主要爬取,电影名字跟评分。只是使用不同方式去对比下不同点,所以数据方面就不过多提取或者保存。只是简单的将其爬取下打印出来看看。

第一:采用多进程 , multiprocessing 模块。 当然这个耗时更网络好坏有关。在全部要请求都正常的情况下耗时15s多。

#!/usr/bin/env python2
# -*- coding=utf-8 -*- from multiprocessing import Process, Queue import time
from lxml import etree
import requests class DouBanSpider(Process):
def __init__(self, url, q):
# 重写写父类的__init__方法
super(DouBanSpider, self).__init__()
self.url = url
self.q = q
self.headers = {
'Host': 'movie.douban.com',
'Referer': 'https://movie.douban.com/top250?start=225&filter=',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
} def run(self):
self.parse_page() def send_request(self,url):
'''
用来发送请求的方法
:return: 返回网页源码
'''
# 请求出错时,重复请求3次,
i = 0
while i <= 3:
try:
print u"[INFO]请求url:"+url
return requests.get(url=url,headers=self.headers).content
except Exception as e:
print u'[INFO] %s%s'% (e,url)
i += 1 def parse_page(self):
'''
解析网站源码,并采用xpath提取 电影名称和平分放到队列中
:return:
'''
response = self.send_request(self.url)
html = etree.HTML(response)
# 获取到一页的电影数据
node_list = html.xpath("//div[@class='info']")
for move in node_list:
# 电影名称
title = move.xpath('.//a/span/text()')[0]
# 评分
score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0] # 将每一部电影的名称跟评分加入到队列
self.q.put(score + "\t" + title) def main():
# 创建一个队列用来保存进程获取到的数据
q = Queue()
base_url = 'https://movie.douban.com/top250?start='
# 构造所有url
url_list = [base_url+str(num) for num in range(0,225+1,25)] # 保存进程
Process_list = []
# 创建并启动进程
for url in url_list:
p = DouBanSpider(url,q)
p.start()
Process_list.append(p) # 让主进程等待子进程执行完成
for i in Process_list:
i.join() while not q.empty():
print q.get() if __name__=="__main__": start = time.time()
main()
print '[info]耗时:%s'%(time.time()-start)

Process多进程

#!/usr/bin/env python2
# -*- coding=utf-8 -*- from threading import Thread
from Queue import Queue
import time
from lxml import etree
import requests class DouBanSpider(Thread):
def __init__(self, url, q):
# 重写写父类的__init__方法
super(DouBanSpider, self).__init__()
self.url = url
self.q = q
self.headers = {
'Cookie': 'll="118282"; bid=ctyiEarSLfw; ps=y; __yadk_uid=0Sr85yZ9d4bEeLKhv4w3695OFOPoedzC; dbcl2="155150959:OEu4dds1G1o"; as="https://sec.douban.com/b?r=https%3A%2F%2Fbook.douban.com%2F"; ck=fTrQ; _pk_id.100001.4cf6=c86baf05e448fb8d.1506160776.3.1507290432.1507283501.; _pk_ses.100001.4cf6=*; __utma=30149280.1633528206.1506160772.1507283346.1507290433.3; __utmb=30149280.0.10.1507290433; __utmc=30149280; __utmz=30149280.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=223695111.1475767059.1506160772.1507283346.1507290433.3; __utmb=223695111.0.10.1507290433; __utmc=223695111; __utmz=223695111.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); push_noty_num=0; push_doumail_num=0',
'Host': 'movie.douban.com',
'Referer': 'https://movie.douban.com/top250?start=225&filter=',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
} def run(self):
self.parse_page() def send_request(self,url):
'''
用来发送请求的方法
:return: 返回网页源码
'''
# 请求出错时,重复请求3次,
i = 0
while i <= 3:
try:
print u"[INFO]请求url:"+url
html = requests.get(url=url,headers=self.headers).content
except Exception as e:
print u'[INFO] %s%s'% (e,url)
i += 1
else:
return html def parse_page(self):
'''
解析网站源码,并采用xpath提取 电影名称和平分放到队列中
:return:
'''
response = self.send_request(self.url)
html = etree.HTML(response)
# 获取到一页的电影数据
node_list = html.xpath("//div[@class='info']")
for move in node_list:
# 电影名称
title = move.xpath('.//a/span/text()')[0]
# 评分
score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0] # 将每一部电影的名称跟评分加入到队列
self.q.put(score + "\t" + title) def main():
# 创建一个队列用来保存进程获取到的数据
q = Queue()
base_url = 'https://movie.douban.com/top250?start='
# 构造所有url
url_list = [base_url+str(num) for num in range(0,225+1,25)] # 保存线程
Thread_list = []
# 创建并启动线程
for url in url_list:
p = DouBanSpider(url,q)
p.start()
Thread_list.append(p) # 让主线程等待子线程执行完成
for i in Thread_list:
i.join() while not q.empty():
print q.get() if __name__=="__main__": start = time.time()
main()
print '[info]耗时:%s'%(time.time()-start)

Thread多线程

#!/usr/bin/env python2
# -*- coding=utf-8 -*- from Queue import Queue
import time
from lxml import etree
import requests
import gevent # 打上猴子补丁
from gevent import monkey
monkey.patch_all() class DouBanSpider(object):
def __init__(self):
# 创建一个队列用来保存进程获取到的数据
self.q = Queue()
self.headers = {
'Cookie': 'll="118282"; bid=ctyiEarSLfw; ps=y; __yadk_uid=0Sr85yZ9d4bEeLKhv4w3695OFOPoedzC; dbcl2="155150959:OEu4dds1G1o"; as="https://sec.douban.com/b?r=https%3A%2F%2Fbook.douban.com%2F"; ck=fTrQ; _pk_id.100001.4cf6=c86baf05e448fb8d.1506160776.3.1507290432.1507283501.; _pk_ses.100001.4cf6=*; __utma=30149280.1633528206.1506160772.1507283346.1507290433.3; __utmb=30149280.0.10.1507290433; __utmc=30149280; __utmz=30149280.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=223695111.1475767059.1506160772.1507283346.1507290433.3; __utmb=223695111.0.10.1507290433; __utmc=223695111; __utmz=223695111.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); push_noty_num=0; push_doumail_num=0',
'Host': 'movie.douban.com',
'Referer': 'https://movie.douban.com/top250?start=225&filter=',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
} def run(self,url):
self.parse_page(url) def send_request(self,url):
'''
用来发送请求的方法
:return: 返回网页源码
'''
# 请求出错时,重复请求3次,
i = 0
while i <= 3:
try:
print u"[INFO]请求url:"+url
html = requests.get(url=url,headers=self.headers).content
except Exception as e:
print u'[INFO] %s%s'% (e,url)
i += 1
else:
return html def parse_page(self,url):
'''
解析网站源码,并采用xpath提取 电影名称和平分放到队列中
:return:
'''
response = self.send_request(url)
html = etree.HTML(response)
# 获取到一页的电影数据
node_list = html.xpath("//div[@class='info']")
for move in node_list:
# 电影名称
title = move.xpath('.//a/span/text()')[0]
# 评分
score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0] # 将每一部电影的名称跟评分加入到队列
self.q.put(score + "\t" + title) def main(self): base_url = 'https://movie.douban.com/top250?start='
# 构造所有url
url_list = [base_url+str(num) for num in range(0,225+1,25)]
# 创建协程并执行
job_list = [gevent.spawn(self.run,url) for url in url_list]
# 让线程等待所有任务完成,再继续执行。
gevent.joinall(job_list) while not self.q.empty():
print self.q.get() if __name__=="__main__":
start = time.time()
douban = DouBanSpider()
douban.main()
print '[info]耗时:%s'%(time.time()-start)

gevent协程

														
		

Python多进程、多线程、协程的更多相关文章

  1. python 多进程&sol;多线程&sol;协程 同步异步

    这篇主要是对概念的理解: 1.异步和多线程区别:二者不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段.异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事 ...

  2. Python 多进程 多线程 协程 I&sol;O多路复用

    引言 在学习Python多进程.多线程之前,先脑补一下如下场景: 说有这么一道题:小红烧水需要10分钟,拖地需要5分钟,洗菜需要5分钟,如果一样一样去干,就是简单的加法,全部做完,需要20分钟:但是, ...

  3. python爬虫——多线程&plus;协程(threading&plus;gevent)

    上一篇博客中我介绍了如何将爬虫改造为多进程爬虫,但是这种方法对爬虫效率的提升不是非常明显,而且占用电脑cpu较高,不是非常适用于爬虫.这篇博客中,我将介绍在爬虫中广泛运用的多线程+协程的解决方案,亲测 ...

  4. python采用 多进程&sol;多线程&sol;协程 写爬虫以及性能对比,牛逼的分分钟就将一个网站爬下来&excl;

    首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务.一个CPU,在一个时间切片里只能运行一个程序. 从操作系统的角度: 进程和线程,都 ...

  5. python多进程与协程

    1.进程的概念 什么是进程->CPU在同一时刻只能处理一个任务,只是因为cpu执行速度很快. cpu在各个任务之间来回的进行切换. 进程的概念:正在进行的一个过程或者说一个任务,而负责执行任务的 ...

  6. python 多进程和协程配合使用

    一.需求分析 有一批key已经写入到3个txt文件中,每一个txt文件有30万行记录.现在需要读取这些txt文件,判断key是否在数据仓库中.(redis或者mysql) 为空的记录,需要写入到日志文 ...

  7. python多进程单线程&plus;协程实现高并发

    并发:看起来像同时运行就是并发 并行:同一时间同时被执行叫做并行,最大并行数就是CPU核数 协程不是实实在在存在的物理基础和操作系统运行逻辑,只是程序员从代码层面避开了系统对遇到IO的程序会切走CPU ...

  8. python开发concurent&period;furtrue模块&colon;concurent&period;furtrue的多进程与多线程&amp&semi;协程

    一,concurent.furtrue进程池和线程池 1.1 concurent.furtrue 开启进程,多进程&线程,多线程 # concurrent.futures创建并行的任务 # 进 ...

  9. python 多线程, 多进程, 协程

    1. 介绍: threading用于提供线程相关的操作,线程是应用程序中工作的最小单元.python当前版本的多线程库没有实现优先级.线程组,线程也不能被停止.暂停.恢复.中断. 2. 1  线程执行 ...

  10. Python多线程、多进程和协程的实例讲解

    线程.进程和协程是什么 线程.进程和协程的详细概念解释和原理剖析不是本文的重点,本文重点讲述在Python中怎样实际使用这三种东西 参考: 进程.线程.协程之概念理解 进程(Process)是计算机中 ...

随机推荐

  1. 循序渐进开发WinForm项目(2&rpar;--项目代码的分析

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  2. hdu 1796 How many integers can you find

    容斥原理!! 这题首先要去掉=0和>=n的值,然后再使用容斥原理解决 我用的是数组做的…… #include<iostream> #include<stdio.h> #i ...

  3. pollard&lowbar;rho和Miller&lowbar;Rabin

    Miller_Rabin就是以概论大小来判断素数 可以判断2^63范围的数 pollard_rho推荐两个很好的博客来理解:整数分解费马方法以及Pollard rho和[ZZ]Pollard Rho算 ...

  4. linux shell read command-Getting User Input Via Keyboard--ref

    ref:http://bash.cyberciti.biz/guide/Getting_User_Input_Via_Keyboard You can accept input from the ke ...

  5. Putty工具包简单使用

    Putty工具包简单使用 一.Putty简介 Putty是一款远程登录工具,用它可以非常方便的登录到Linux服务器上进行各种操作(命令行方式).Putty完全免费,而且无需安装(双击即可运行),支持 ...

  6. zookeeper笔记--配置以及和spark hbase结合使用

    Spark集群基于ZooKeeper的搭建:http://www.dataguru.cn/thread-333245-1-1.html Spark需要修改的地方: 进入spark的配置目录,参照下面代 ...

  7. iOS开发之使用Ad Hoc进行测试

    由于最近某个项目需要给别人测试,使用的是Ad Hoc方法 首先登录开发者官网配置证书 1.添加Certificates,从电脑获取certSigningRequest然后添加进去 2.在Identif ...

  8. isinstance和issubclass、动态模块导入、异常处理

    一.isinstance和issubclass isinstance:判断某个对象是否是某个类的实例,返回True或Flase issubclass:判断某个类是否是某个类的子类. 例如: class ...

  9. Mixing a dll boost library with a static runtime is a really bad idea错误的解决

    作者:朱金灿 来源:http://blog.csdn.net/clever101 同事在使用boost库时遇到一个问题,在编译时出现一个错误:Mixing a dll boost library wi ...

  10. Web项目发布的更新

    在项目发版后经常需要修改bug,解决出现的各种问题,对项目升级,这时候就需要将之前部署,上线的项目更新版本. 本文就简单说一下一些出现的关键点(不到之处欢迎指教) 1.使用idea,eclipse等工 ...