1.异步同步 和 阻塞 非阻塞
线程的三种状态:
1.就绪
2.运行
3.阻塞
阻塞:遇到了IO操作 代码卡住 无法执行系一行 cpu会切换到其他任务
非阻塞:与阻塞相反 代码正在执行
阻塞和非阻塞描述的是运行的状态
同步:
提交任务必须等待任务完成,才能执行下一行
异步:
提交任务不需要等待任务完成,立即执行下一行
指的是一种提交任务的方式
2.异步回调
为什么需要回调?
子进程帮助主进程完成任务 处理任务的结果应该交还给主进程
子线程帮助主线程完成任务 处理任务的结果不需要交还给主线程
其他方式也可以将数据交还给主进程:
1.shutdown 主进程回等到所有任务完成
2.result 函数 会阻塞直到任务完成
都会阻塞 导致效率降低 所以用回调
注意: 回调函数什么时候会被执行? 子进程任务完成时
谁在执行回调函数? 主进程或主线程
线程的异步回调:使用方式都相同 唯一的不同是执行回调函数 是子线程在执行
举例(进程异步回调,线程异步回调的时候用):
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from threading import current_thread
import os
pool=ProcessPoolExecutor(2)#开启一个进程池
import requests
def get_data_task(url):
print(os.getpid(),"生产数据!")
response=requests.get(url)
text=response.content.decode("utf-8")
print(text)
return text
def parser_data(f):
print(os.getpid(),"处理数据")
print("正在解析:长度%s"%len(f.result()))
urls=[
"http://www.baidu.com",
"http://www.baidu.com",
"http://www.baidu.com",
"http://www.baidu.com"
]
if __name__ == '__main__':
for url in urls:
f=pool.submit(get_data_task,url)#提交任务会返回一个对象 用于回去执行状态和结果
#pool.shutdown() #等目前任务完成后,才能提交新任务
#方式1:添加列表res (自己来保存数据 并执行shutdown 仅在多线程)
#print(f.result())#方式2 result时阻塞的知道任务完成
f.add_done_callback(parser_data)#进程回调是主进程执行的
#由于数据需要交互 进程间资源不共享 故需要主进程来进行数据解析
print("over")
3.线程队列
队列: 遵循先进献出原则 q=queue() q.put("1") q.get()
堆栈队列: 遵顼 先进后出原则 函数调用就是进栈 函数结束就出栈 递归回造成栈的溢出
q=queue.LifoQueue() q.put("1") q.get()
优先级队列:遵循的优先级数值越小 越优先 优先级相同时按照ASSCII码决定优先
priorityqueue
4.协程
为什么出现协程?
因为Cpython由于GIL 导致同一时间只有一个线程在跑 意味如果你的程序在计算密集时
多线程效率也不会提升;如果是io密集时 有没有在单线程下实现并发 :
1.没有 开启多线程来处理io 子线程遇到io cpu切走 但是不一定会切到主线
2.有 如果可以程序在遇到iio的时候转而去计算 这样一来就可以保证cpu一直在处
理你的程序 当然如果时间过长cpu就会切走
总结:单线程下实现并发 将io阻塞时间用于执行计算 可以提高效率 原理是一直使用到
程序执行结束或者执行时间超时。
怎么实现单线程并发?
并发指的是 看起来像是同时运行 实际是在任务间来回切换 同时许哟啊保存执行的状态
任务是一堆代码 可以用函数装起来
1.如何让两个函数切换执行
yiela可以保存函数的执行状态
通过生成器可以实现伪并发
并发不一定提升效率 反而会降低效率 当任务全是计算时
2.如何知道发生了io从而切换执行
目前我们无法实现
第三方模块 greenlet 可以实现并发 但是不能检测io
第三方模块 gevent 封装greenlet 可以实现单线程并发 并且能狗检测io操作 自动切换
5.协程中的两个模块 greenlet gevent
greenlet:
1.实例化greenlet 得到一个对象 传入要执行的任务 至少存在两个任务
2.先让某个任务执行起来 使用对象调用switch
3.在任务的执行过程中 手动调用swith来切换
4.可以实现并发 但是不能检测io阻塞
举例:
import greenlet
def task1():
print("task1 1")
g2.switch()
print("task1 2")
def task2():
print("task2 1")
g1.swith()
print("task2 2")
g1=greenlet.greenlet(task1)
g2=greenlet.greenlet(task2)
gevent:
1.spawn函数传入你的任务
2.调用join去开启任务
3.检测io操作要打monkey补丁 就是一个函数 在程序最开始的地方调用
举例:
from gevent import monkey
monkey.patch_all()
import gevent
import time
def task1():
print("task1 1")
time.sleep(2)
#gevent.sleep(1)
print("task1 2")
def task2():
print("task2 1")
time.sleep(1)
#gevent.sleep(2)
print("task2 2")
g1 = gevent.spawn(task1)
g2 = gevent.spawn(task2)
# g1.join()
# g2.join()
gevent.joinall([g1,g2])
print("主")
协程的应用场景:
TCP 多客户端实现方式
1. 来一个客户端就有一个进程 资源消耗较大
2. 来一个客户端就来一个线程 也不能无限开
3. 用进程池 或 线程池 还是一个线程或进程只能维护一个连接
4. 协程 一个线程就可以处理多个客户端 遇到io就切到另一个