线程、进程、协程

线程是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、简言之:主进程一死,守护进程比跟着死

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐