同步和异步两种执行方式
-
进行运行的三种状态:运行、就绪、阻塞
-
阻塞、非阻塞、同步、异步
- 阻塞(程序运行的角度):程序运行时,遇到IO,程序挂起,cpu被切走
- 非阻塞(程序运行的角度):程序没有遇到IO;程序遇到IO但我通过某种手段,让cpu强行运行我的程序
- 同步(提交任务的角度):提交一个任务,自任务开始运行直到此任务结束(可能有IO)返回一个返回值之后,我在提交下一个任务(示例:先告知第一个老师完成写书任务,我从原地等待,等他两天之后完成了,告诉完事了,我再发布下一个任务。。。)
- 异步(提交任务的角度):一次提交多个任务,然后我就执行下一行代码(示例:直接将任务告知三个老师,我就忙我的,直到三个老师完成之后告知我)
- 异步返回结果如何回收?
-
同步调用与异步调用:
-
异步调用:
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time import random import os def task(i): print(f'{os.getpid()}开始任务') time.sleep(random.randint(1,3)) print(f'{os.getpid()}任务结束') return i if __name__=='__main__': #异步调用 pool=ProcessPoolExecutor() for i in range(10): pool.submit(task,i) pool.shutdown(wait=True) #shutdown:让我的主进程等待进程池中所有的子进程都结束任务之后,再执行,有点类似与join #shutdown:在上一个进程池没有完成所有的任务之前,不允许添加新的任务 #一个任务时通过一个函数实现的,任务完成得返回值就是函数得返回值 print('====主')
-
同步调用:
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time import random import os def task(i): print(f'{os.getpid()}开始任务') time.sleep(random.randint(1,3)) print(f'{os.getpid()}任务结束') return i if __name__ == '__main__': pool=ProcessPoolExecutor() for i in range(10): obj=pool.submit(task,i) #obj是一个动态对象,返回当前的而对象状态,有可能运行中,有可能(就绪,堵塞)还有可能是结束了 obj.result()#必须等到这个任务完成后,返回了结果之后,在执行下一个任务 print(f'任务结果{obj.result()}') pool.shutdown(wait=True) #shutdown:让我的主进程等待进程池中所有的子进程都结束任务之后,在执行,有点类似join #shutdown:在上一个进程池没有完成所有的任务之前,不允许添加新的任务 #一个任务是通过一个函数实现的,任务完成了,他的返回就是函数的返回值 print('====主')
-
-
异步如何取结果?
-
第一种方式:统一回收结果
-
方式一:异步调用,统一回收结果 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import time import os import random def task(i): print(f'{os.getpid()}任务开始') time.sleep(random.randint(1,3)) print(f'{os.getpid()}任务结束') return i if __name__ == '__main__': l1=[] pool=ProcessPoolExecutor() for i in range(10): obj=pool.submit(task,i) l1.append(obj) pool.shutdown(wait=True) print(l1) for i in l1: print(i.result()) print('全') #统一回收结果:我不能马上收到任何一个已经完成任务的返回值,我只能等到所有任务结束统一回收
-
-
第二种方式:一个个回收
import requests def task(url): """模拟的就是爬取多个源代码,一定有IO操作""" ret=requests.get(url) if ret.status_code==200: return ret.text def parse(content): """模拟对数据进行分析 一般没有IO 如果都有IO用回调函数就没有意义""" return len(content) if __name__ == '__main__': """串行,耗费时间长""" ret=task('http://www.baidu.com') print(parse(ret)) ret = task('http://www.JD.com') print(parse(ret))
-
-
异步调用+回调函数
-
浏览器工作原理:向服务器发送一个请求,服务端验证你的请求,如果正确,给你的浏览器返回一个文件,浏览器接收到文件,将文件里面的代码渲染成你看到的漂亮美丽的模样
-
什么叫爬虫?
- 利用代码模拟一个浏览器,进行浏览器的工作流程得到一堆源代码
- 对源代码进行数据清洗得到我想要的数据
-
pip模块为第三方模块,安装里面requests模块
-
模拟浏览器
import requests#导入requests模块 ret=request.get('http://www.baidu.com')# if ret.status_code==200:#如果请求状态是成功 print(ret.text)#则把源码打印出来
-
-
异步调用+回调函数(版本一)
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time import random import os import requests def task(url): """模拟的就是爬去多个源代码 一定有IO操作""" ret=requests.get(url) if ret.status_code==200: return ret.text def parse(content): """模拟对数据进行分析 一般没有IO 如果有IO 会回调函数就没有意义""" return len(content) if __name__ == '__main__': """开启线程池,并发并行的执行""" url_list = [ 'http://www.baidu.com', 'http://www.JD.com', 'http://www.JD.com', 'http://www.JD.com', 'http://www.taobao.com', 'https://www.cnblogs.com/jin-xin/articles/7459977.html', 'https://www.luffycity.com/', 'https://www.cnblogs.com/jin-xin/articles/9811379.html', 'https://www.cnblogs.com/jin-xin/articles/11245654.html', 'https://www.sina.com.cn/', ] pool=ThreadPoolExecutor(4) obj_list=[] for url in url_list: obj=pool.submit(task,url) obj_list.append(obj) pool.shutdown(wait=True) for res in obj_list: print(parse(res.result())) print("===主") #版本一: #1.异步发出10个任务,并发的执行,但是统一的接受所有的任务返回值(效率低,不能实时的获取结果) #2.分析结果流程是串行,影响效率 #for res in obj_list: # print(parse(res.result()))
-
异步调用+回调函数(版本二)
#版本二:针对版本一的缺点2,让串行变成并发或者并行 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time import random import os import requests def task(url): """模拟的就是爬取多个源代码 一定有IO操作""" ret=requests.get(url) if ret.status_code==200: return parse(ret.text) def parse(content): return len(content) if __name__ == '__main__': url_list = [ 'http://www.baidu.com', 'http://www.JD.com', 'http://www.JD.com', 'http://www.JD.com', 'http://www.taobao.com', 'https://www.cnblogs.com/jin-xin/articles/7459977.html', 'https://www.luffycity.com/', 'https://www.cnblogs.com/jin-xin/articles/9811379.html', 'https://www.cnblogs.com/jin-xin/articles/11245654.html', 'https://www.sina.com.cn/', ] pool=ProcessPoolExecutor(4) obj_list=[] for url in url_list: obj=pool.submit(task,url) obj_list.append(obj) ''' # 1 在开一个线程进程池,并发并行的处理. 再开一个线程进程池,开销大. # 2 将原来的任务扩大, 版本一: 线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码, 并发执行, 最后统一用列表回收10个任务, 串行着分析源码. 版本二: 线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码+数据分析, 并发执行, 最后将所有的结果展示出来. 耦合性增强了. 并发执行任务,此任务最好是IO阻塞,才能发挥最大的效果 ''' pool.shutdown(wait=True) for res in obj_list: print(res.result())
-
异步调用+回调函数(版本三)
-
#版本三: #基于异步调用回收所有任务结果我要做实时回收结果 #并发执行任务每个任务只是处理IO阻塞,不能增加新功能 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time import random import os import requests def task(url): """模拟就是爬去多个源代码 一定有IO操作""" ret=requests.get(url) if ret.status_code==200: return ret.text def parse(obj): print(len(obj.result())) if __name__ == '__main__': # 开启线程池,并发并行的执行 url_list = [ 'http://www.baidu.com', 'http://www.JD.com', 'http://www.JD.com', 'http://www.JD.com', 'http://www.taobao.com', 'https://www.cnblogs.com/jin-xin/articles/7459977.html', 'https://www.luffycity.com/', 'https://www.cnblogs.com/jin-xin/articles/9811379.html', 'https://www.cnblogs.com/jin-xin/articles/11245654.html', 'https://www.sina.com.cn/', ] pool=ThreadPoolExecutor(4) for url in url_list: obj=pool.submit(task,url) obj.add_done_callback(parse) """ 线程池设置4个线程,异步发起10个任务,每个任务都是通过网页获取源码,并发执行 当一个任务完成之后,将parse这个分析代码的任务交由剩余的空闲的线程去执行,你这个线程继续处理其他任务 如果进程池+回调:回调函数由主进程去执行 如果线程池+回调:回调函数由空闲线程去执行 """
-
-
异步和回调的区别?
- 异步是站在发布任务的角度
- 回调:站在接受结果的角度:回调函数 按顺序接受每个任务结果,进行下一步操作
- 异步处理IO类型 回调处理非IO类型
-