今天学习了异步同步、阻塞非阻塞、异步回调、线程队列和协程
一、异步同步和阻塞非阻塞
线程的三种状态:
1、就绪
2、运行
3、阻塞
阻塞:遇到了IO操作 代码卡住 无法执行下一行 CPU会切换到其他任务
非阻塞: 与阻塞相反 代码正在执行(运行状态) 或处于就绪状态
阻塞和非阻塞描述的是运行的状态
同步:提交任务必须等待任务完成,才能执行下一行
异步:提交任务不需要等待任务完成,立即执行下一行
指的是一种提交任务的方式
二、异步回调
为什么回调:子进程帮助主进程完成任务 处理任务的结果应该交还给主进程
其他方式也可以将数据交还给主进程
1、shutdown 主进程会等到所有任务完成
2、result函数 会阻塞直到任务完成
注意:
回调函数什么时候被执行?子进程完成时
谁在执行回调函数?主进程
线程的异步回调
使用方式都相同 唯一的不同是执行回调函数 是子线程在执行
#进程利用回调完成生产者消费者from concurrent.futures import ProcessPoolExecutor
import os
pool = ProcessPoolExecutor()
#爬虫 从网络某个地址获取一个HTML文件
import requests #该模块用于网络请求
#生产数据
def get_data_task(url):
print(os.getpid(),'正在生产数据!')
response = requests.get(url)
text = response.content.decode('utf-8')
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)
f.add_done_callback(parser_data) #回调函数是主进程在执行
#因为子进程是负责获取数据的 然而数据怎么处理 子进程并不知道
应该把数据还给主进程
print('over')
#线程利用回调完成生产者消费者
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread
pool = ThreadPoolExecutor
#爬虫 从网络某个地址获取一个HTML文件
import requests #该模块用于网络(HTTP)请求
#生产数据
def get_data_task(url):
print(current_thread(),'正在生产数据!')
response = requests.get(url)
text = response.content.decode('utf-8')
return text
#处理数据
def parser_data(f):
print(current_thread(),'处理数据')
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)
f.add_done_callback(parser_data) #因为是子线程在执行回调函数 所以没有主次之分 任何子线程都可以对函数进行回调
print('over')
三、线程队列
import queue
#普通队列 先进先出
q = queue.Queue()
q.put('a')
q.put('b')
print(q.get())
print(q.get())
#堆栈队列 先进后出 函数调用就是进栈 函数结束就出栈 递归造成栈溢出
q2 = queue.LifoQueue()
q2.put('a')
q2.put('b')
print('q2.get()')
#优先级队列
q3 = queue.PriorityQueue() #数值越小优先级越高 优先级相同时 比较大小 小的先取
q3.put((-100,'c'))
q3.put((1,'a'))
q3.put((100,b))
print(q3.get())
四、协程
协程的目的是在单线程下实现并发
单线程下实现并发 将io阻塞时间用于执行计算 可以提高效率 原理:一直使用CPU直到超时
怎么实现单线程并发?
并发 指的是 看起来像是同时运行 实际是在任务间来回切换 同时需要保存执行的状态
任务是一堆代码 可以用函数装起来
1.如何让两个函数切换执行
yield可以保存函数的执行状态
通过生成器可以实现伪并发
并发不一定提升效率 反而会降低效率 当任务全是计算时
2.如何知道发生了io?从而切换执行
目前咱们实现不了。。
第三方模块 greenlet 可以实现并发 但是不能检测io
第三方模块 gevent 封装greenlet 可以实现单线程并发 并且能够检测io操作 自动切换
#用yield实现两个函数切换执行
import time
def task():
while True:
print('task1')
time.sleep(4)
yield 1
def task2():
g = task()
while True:
try:
print('task2')
next(g)
except Exception:
print('任务完成')
break
task2()
#使用greenlet模块实现并发
import greenlet
import time
def task1():
print('task1 1')
time.sleep(2)
g2.switch()
print('task1 2')
g2.swith()
def task2():
print('task2 1')
g1.switch()
print('task2 2')
g1 = greenlet.greenlet(task1)
g2 = greenlet.greenlte(task2)
g1.switch()
#1.实例化greenlet得到一个对象 传入要执行的任务
#2.先让某个任务执行起来 使用对象调用switch
#3.在任务的执行过程中 手动调用switch来切换
#使用gevent模块实现单线程的并发
from gevent import monkey
monkey.patch_all()
import gevent
import time
def eat():
print('eat food 1')
time.sleep(2)
print('eat food 2')
def play():
print('play 1')
time.sleep(1)
print('play 2')
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
gevent.joinall([g1,g2])
print('主')
#1.spawn函数传入你的任务
#2.调用join 去开启任务
#3.检测io操作需要打mokey补丁 就是一个函数 在程序最开始的地方调用它
来源:https://www.cnblogs.com/xiaocaiyang/p/9954077.html