CPU 调度 进程 、线程、协程 、python 协程 守护进程 asyncio aiohttp, aiofiles
·
线程、进程、协程
线程是CPU调度的最小单元,进程是资源分配的最小单元,协程是单线程下的开发,又称微线程
线程、进程都是操作系统执行的,协程的操作才是程序员
进程:切换需要的资源最大,效率最低
线程:切换需要的资源一般,效率一般
协程:切换需要的资源最小,效率高
多线程适合IO密集型操作(文件读写、爬虫,网络请求),多进程适合CPU密集型操作(音频、视频、科学计算)
进程、线程、协程都是可以完成多任务的。
python >> greenlet
greenlet 是 Python 中的一个库,它提供了轻量级的协程(微线程)支持,允许你在同一个线程内手动暂停和恢复多个独立的执行流,
是实现复杂并发控制的基础。它最著名的应用是作为 gevent 和 eventlet 等高并发网络库的底层核心
启动与切换
通过调用 greenlet.switch() 方法来启动或切换到目标 greenlet。第一次对一个 greenlet 调用 switch() 时,它会开始运行其绑定的函数。
之后调用 switch() 则会让它从中断的地方恢复
# pip install greenlet 安装
from greenlet import greenlet
def write():
print('我准备写作业拉!!')
gr.switch() # 启动 gr
print('我写完作业拉!')
gr.switch() # 切换到 gw
def read():
print('我准备阅读拉!!')
gw.switch() # 切换到 gw
print('我书读完拉')
if __name__ == '__main__':
gw = greenlet(write)
gr = greenlet(read)
gw.switch() # 启动gw
#gr.switch()
## 执行过程
# gw.switch() 启动 gw,开始执行 write,打印 "我准备写作业拉!!"。
# write中调用 gr.switch(),gw 暂停,切换到 gr,打印 "我准备阅读拉!!"。
# read 中调用 gw.switch(),gr 暂停,切换回 gw,gw 从中断的地方恢复,打印 "我写完作业拉!"。
# write 中调用 gr.switch(),gw 支持完毕(gw变为死亡状态 gw.dead 为 True),切换回 gr,gr 从中断的地方恢复,打印 "我书读完拉"。
# read 执行完毕,gr 变为死亡状态(gr.dead 为 True),控制权返回给它的父 greenlet(即主 greenlet)
from greenlet import greenlet, getcurrent
def test1():
data = gr2.switch("来自 gr1 的消息")
print(f"gr1 收到: {data}")
return "gr1 结束"
def test2(x):
print(f"gr2 收到: {x}")
y = getcurrent().parent.switch("来自 gr2 的回复")
print(f"gr2 再次收到: {y}")
gr1 = greenlet(test1)
gr2 = greenlet(test2)
result = gr1.switch()
print(f"最终返回值: {result}")
##### 结果
gr2 收到: 来自 gr1 的消息
最终返回值: 来自 gr2 的回复
# 你可以使用 greenlet.throw() 方法向一个 greenlet 中抛出异常,常用于中断它的执行
from greenlet import greenlet
def run_forever():
try:
while True:
print("运行中...")
greenlet.getcurrent().parent.switch()
except Exception as e:
print(f"捕获到异常: {e}")
g = greenlet(run_forever)
g.switch() # 启动
g.switch() # 再次切换,让它继续运行
g.throw(Exception("出错了!")) # 向 g 中抛出异常
# 执行结果
运行中...
运行中...
捕获到异常: 出错了!
python >> gevent


import gevent
def write():
print('我准备写作业拉!!')
gevent.sleep(1)
print('我写完作业拉!')
def read():
print('我准备阅读拉!!')
gevent.sleep(2)
print('我书读完拉')
if __name__ == '__main__':
gw = gevent.spawn(write)
gr = gevent.spawn(read)
gw.join()
gr.join()
## 结果
我准备写作业拉!!
我准备阅读拉!!
我写完作业拉!
我书读完拉
》》》猴子补丁
import gevent
# 猴子补丁
from gevent import monkey
monkey.patch_all() # 打上所有补丁(必须在导入其他库之前)
# # 此后,标准的socket、time、request等模块都变成了非阻塞的
import time
def write():
print('我准备写作业拉!!')
#gevent.sleep(1)
time.sleep(1) # 用了 猴子补丁 此句会自动切换 gevent.sleep(1)
print('我写完作业拉!')
def read():
print('我准备阅读拉!!')
# gevent.sleep(2)
time.sleep(2) # 用了 猴子补丁 此句会自动切换 gevent.sleep(1)
print('我书读完拉')
if __name__ == '__main__':
gw = gevent.spawn(write)
gr = gevent.spawn(read)
gw.join()
gr.join()
######结果=============
我准备写作业拉!!
我准备阅读拉!!
我写完作业拉!
我书读完拉
》》》超时控制
# 方式一:gevent.Timeout 上下文管理器
import gevent
from gevent import Timeout
def fetch_data():
gevent.sleep(3) # 模拟慢请求
return "data"
timeout = Timeout(2) # 设置2秒超时
try:
timeout.start()
result = fetch_data()
except Timeout:
print("请求超时了!")
finally:
timeout.cancel() # 记得取消
# 方式二:更优雅的 with 语句
import gevent
from gevent import Timeout
def fetch_data():
gevent.sleep(3)
return "data"
try:
with Timeout(2):
result = fetch_data()
except Timeout:
print("请求超时了!")
asyncio
# 协程函数(coroutine Function):使用async关键字修饰的函数,就是协程函数
# 协程对象(coroutine Object ):调用协程函数,就会得到 协程对象
# 注意 :调用协程函数,并不会执行协程函数中的代码
import asyncio
# 定义一个协程函数
async def work():
print("work---A")
print("work---B")
return 'work---over'
# 调用协程函数,会得到协程对象
coroutine_obj = work()
# asyncio.run 方法做了3件事
# 1. 创建一个事件循环
# 2. 将收到的协程对象,包装成一个任务(Task),交给事件循环
# 3. 启动事件循环
# 注意 asyncio.run() 会阻塞当前线程,直到任务执行完毕,并返回该任务 return 的最终结果。
result = asyncio.run(coroutine_obj)
print(result)

# 多任务 同步执行 阻塞的
import asyncio
# 定义一个协程函数
async def work(n,delay):
print(f'work {n}--开始了')
# 模拟一个IO等待
await asyncio.sleep(delay)
print(f'work{n} end')
return f'work{n}---结果'
async def main():
# 此时会等待 work(x,xx)执行完成
res1 = await(work(1,2))
print(res1)
# 等待上面执行完成后,再等待work(x,xx)执行完成
res2 = await(work(2,2))
print(res2)
# 等待上面执行完成后,再等待work(x,xx)执行完成
res3 = await(work(3,2))
print(res3)
print('main --end')
return 'main- 返回结果'
if __name__ == '__main__':
# 将协程对象交给事件循环
res = asyncio.run(main())
print(res)
==结果
work 1--开始了
work1 end
work1---结果
work 2--开始了
work2 end
work2---结果
work 3--开始了
work3 end
work3---结果
main --end
main- 返回结果
# 多任务 异步执行 不阻塞
import asyncio
async def work(n,delay):
print(f'work {n}--开始了')
await asyncio.sleep(delay)
print(f'work{n} end')
return f'work{n}---结果'
async def main():
# asyncio.create_task 会把一个协程对象包装成一个可被事件循环调度的任务,并注册到事件循环中
task1 = asyncio.create_task(work(1,2))
task2 = asyncio.create_task(work(2,2))
task3 = asyncio.create_task(work(3,2))
# 此处会等待task1 执行完成
res1 = await task1
print(res1)
# 此处会等待上面task1 执行完成后,再等待task2 完成
res2 = await task2
print(res2)
# 此处会等待上面task2 执行完成后,再等待task3 完成
res3 = await task3
print(res3)
print('main --end')
return 'main- 返回结果'
if __name__ == '__main__':
# 将协程对象交给事件循环
res = asyncio.run(main())
print(res)
work 1--开始了
work 2--开始了
work 3--开始了
work1 end
work2 end
work3 end
work1---结果
work2---结果
work3---结果
main --end
main- 返回结果
# 多任务 异步执行
import asyncio
async def work(n,delay):
print(f'work {n}--开始了')
await asyncio.sleep(delay)
print(f'work{n} end')
return f'work{n}---结果'
async def main():
# task1 = asyncio.create_task(work(1,2))
# task2 = asyncio.create_task(work(2,2))
# task3 = asyncio.create_task(work(3,2))
# 把多个协程对象同时丢给事件循环,并在全部执行完后,一次性拿个所有结果
results = await asyncio.gather(work(1,2),work(2,2),work(3,2))
print(results)
print('main --end')
return 'main- 返回结果'
if __name__ == '__main__':
res = asyncio.run(main())
print(res)
work 1--开始了
work 2--开始了
work 3--开始了
work1 end
work2 end
work3 end
['work1---结果', 'work2---结果', 'work3---结果']
main --end
main- 返回结果
传统下载图片
# 传统方法下载图片
import requests
def download_picture(url):
print(f"图片开始下载:{url}")
# 发送网络请求,获取图片资源
response = requests.get(url)
print('图片下载完成!')
# 保存图片
with open('./imgs/'+url[-10:], 'wb') as f:
f.write(response.content)
url_list=[
'https://scpic.chinaz.net/files/pic/pic9/201509/apic14744.jpg',
'https://pic1.zhimg.com/v2-d9eee914f5f31e88b9300870afb78b23_720w.jpg',
'https://scpic.chinaz.net/files/pic/pic9/201311/apic2253.jpg'
]
if __name__ == '__main__':
for url in url_list:
download_picture(url)
##########结果################
图片开始下载:https://scpic.chinaz.net/files/pic/pic9/201509/apic14744.jpg
图片下载完成!
图片开始下载:https://pic1.zhimg.com/v2-d9eee914f5f31e88b9300870afb78b23_720w.jpg
图片下载完成!
图片开始下载:https://scpic.chinaz.net/files/pic/pic9/201311/apic2253.jpg
图片下载完成!
协程下载图片
# 协程下载图片
import aiohttp, asyncio
async def download_picture(session,url):
print(f"图片开始下载:{url}")
# 发送网络请求,获取图片资源,请求发出后,要等待服务器把数据返回,等的这段事件就是IO等待
response = await session.get(url)
# 等待数据(图片数据可能分多次传输,需要等待数据全部读完,等的这段时间也是IO等待)
content = await response.read()
print('图片下载完成!')
# 保存图片
with open('./imgs/'+url[-10:], 'wb') as f:
f.write(content)
# 释放连接资源(告诉 aiohttp ,这个连接我不用了,可以回收了)
response.close()
async def main():
url_list = [
'https://scpic.chinaz.net/files/pic/pic9/201509/apic14744.jpg',
'https://pic1.zhimg.com/v2-d9eee914f5f31e88b9300870afb78b23_720w.jpg',
'https://scpic.chinaz.net/files/pic/pic9/201311/apic2253.jpg'
]
# 创建会话对象(发请求的工具)
session = aiohttp.ClientSession()
# 创建多个协程对象
# coroutine_list = [download_picture(session,url) for url in url_list]
# await asyncio.gather(*coroutine_list)
# 上面写成一句代码
# 将多个协程对象交给事件循环
await asyncio.gather(*[download_picture(session,url) for url in url_list])
# 关闭会话
await session.close()
if __name__ == '__main__':
asyncio.run(main())
########################结====果#######################
图片开始下载:https://scpic.chinaz.net/files/pic/pic9/201509/apic14744.jpg
图片开始下载:https://pic1.zhimg.com/v2-d9eee914f5f31e88b9300870afb78b23_720w.jpg
图片开始下载:https://scpic.chinaz.net/files/pic/pic9/201311/apic2253.jpg
图片下载完成!
图片下载完成!
图片下载完成!
守护进程
1、一种“依附于主进程存在的子进程”,一旦主进程结束,它就会被自动终止
2、简言之:主进程一死,守护进程比跟着死
更多推荐






所有评论(0)