阻塞 非阻塞 同步 异步
进程运行的三个状态:运行, 就绪,阻塞
两个角度分析 执行和提交任务
执行角度
阻塞 : 程序运行时遇到 IO,程序挂起,CPU 被切走
非阻塞:程序没有遇到 IO ,程序遇到 IO 但是我通过某种手段,让 CPU 强行运行我的程序
提交任务的角度
同步:提交一个任务,自任务开始运行,直到此任务结束,(可能有 IO)返回一个返回值之后,我再提交下一个任务
异步:提交多个任务,我就直接执行下一行代码
返回结果如何回收??
举个例子说明一下什么是同步和异步
同步:
先告知第一个老师完成写书的任务,我在原地等待,等待两三天之后完成后,告诉完事了,我再发布下一个任务
异步:
直接将三个任务告知三个老师,我就忙我的,直到三个老师完成后,告知我
同步异步 代码层的分析
1.异步调用
# 1. 异步调用
# 异步调用返回值如何接收? 未解决
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
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__':
pool = ProcessPoolExecutor ()
for i in range(10):
pool.submit(task,i)
pool.shutdown(wait = True)
#shutdown 让我的主进程等待进程池中所有的子进程都结束任务后
#再执行,有点类似于 join
# 在上一个进程池没有完成所有的任务之前,不允许添加新的任务
# 一个任务是通过函数实现的,任务完成了他的返回值就是函数的返回值
print('====主')
结果是一次性给的
2.同步调用
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)
print('===主')
结果是一个一个来的
**3.异步如何返回结果 **
方式一:异步调用,统一回收结果
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
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__':
pool = ProcessPoolExecutor()
l1 = []
for i in range(10):
obj = pool.submit(task,i)
l1.append(obj)
pool.shutdown(wait = True)
for i in l1:
print(i.result())
print('====主')
## 统一回收结果:我不能马上收到任何一个已经完成的任务的返回值,
# 我只能等到所有的任务全部结束后统一回收
方式二:
异步调用 + 回调函数
爬虫
浏览器工作原理,向服务端发送一个请求,服务端验证你的请求,如果正确,给你的浏览器返回一个文件,浏览器接收到文件,将文件里的代码渲染成你看到的漂亮模样
什么叫 爬虫??
- 利用代码模拟一个浏览器,进行浏览器的工作流程得到一堆源代码
- 对源代码进行数据清洗得到我想要的数据
import requests
ret = requests.get('http://www.baidu.com')
if ret.status_code == 200:
print(ret.text) # 得到源代码
版本一:
版本一:
1. 异步发出10个任务,并发的执行,但是统一的接收所有的任务的返回值.(效率低,不能实时的获取结果)
2. 分析结果流程是串行,影响效率.
for res in obj_list:
print(parse(res.result()))
版本一:
线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码, 并发执行,
最后统一用列表回收10个任务, 串行着分析源码.(没有对数据进行操作)
代码分析
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time
import random
import os
import requests
def task(url):
ret = requests.get(url)
if ret.status_code == 200:
return ret.text
def parse(content):
return len(content)
if __name__ == '__main__':
ret = task('http://www.baidu.com')
print(parse(ret))
ret = task('http://www.JD.com')
print(parse(ret))
ret = task('http://www.taobao.com')
print(parse(ret))
ret = task('https://www.cnblogs.com/jin-xin/articles/7459977.html')
print(parse(ret))
# 开启线程池,并发并行的执行
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()))
版本二
版本二:
线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码+数据分析, 并发执行,
最后将所有的结果展示出来.
耦合性增强了.
并发执行任务,此任务最好是IO阻塞,才能发挥最大的效果
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time
import random
import os
import requests
def task(url):
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 = 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(res.result())
2381
100180
100180
100180
143902
47067
2708
21634
48110
571508
版本三
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):
'''模拟对数据进行分析 一般没有IO'''
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