协程,lscpu
文章目录
- lscpu,thread(s) per core, Core(s) per socket,Socket(s)
- 什么是协程?协程是一种运行在线程里的、可以暂停和恢复的任务。事件循环负责在多个协程之间切换
- 多线程 多进程 concurrent.futures.Future
- Task 可以理解成:Event Loop 管理的“正在执行的协程任务”。协程对象不一定必须手动变成 Task,Event Loop 才能执行。很多情况下,Event Loop 会自动把协程对象包装成 Task
- 谁执行协程?谁负责切换协程?事件循环 Event Loop
- Future 是一个“未来结果的占位盒子”。现在没有结果,但是以后会有结果。协程对象描述“要做什么”;Task负责“什么时候做”;Future负责“做完得到什么”。
- asyncio 是什么?Python 官方提供的异步编程框架
- 什么是事件循环?事件循环(Event Loop)就是一个不断循环运行的调度器,负责检查有哪些任务可以执行,然后安排它们运行。它是 asyncio 异步编程的核心。事件循环绑定并运行在线程中,是运行在线程中的一个调度器,而线程属于某个进程
- 创建 Event Loop 有几种方式?
- loop.stop():让事件循环停止运行。loop.close():关闭事件循环,释放资源,让它不能再使用。
- await只能写在哪里?后面可以跟什么?跟不同内容的作用分别是什么 await 到底是什么意思?
-
- await 会暂停当前协程,把后面那个异步对象交给 Event Loop 去推进执行;当前协程等待它完成后,再继续往下执行
- “await等待的那个异步对象(协程对象,Task,featur)完成”,后,Event Loop 会知道,然后自动恢复当前协程,不需要你手动通知。
- await 后面的东西,不一定会被 Event Loop 立即执行。await 只是建立一个“等待关系”:当前协程暂停,等待这个对象完成。至于这个对象什么时候执行、是否已经在执行,要看它是什么类型(协程对象,task,future).await 后面必须是:Coroutine对象,Task,Future
- await 只能写在 async def 定义的协程函数里面
- asyncio.run() 创建 Event Loop → 执行一个协程 → 等协程结束 → 关闭 Event Loop。返回最外层协程的 return 值
- asyncio.create_task()。把一个 Coroutine 包装成 Task,并立即提交给当前 Event Loop 调度。返回 Task对象。可await,不await
- asyncio.gather() 同时运行多个协程,并等待它们全部完成,然后返回所有结果。返回future,需要await. await后返回一个列表,里面是每个任务的结果。
- asyncio.sleep() 异步睡眠。返回一个协程对象,不会立即执行,需要await
- 什么叫“可等待对象(Awaitable)”? 可等待对象,就是可以被 await 等待的东西
- 线程 A 不能直接操作线程 B 的事件循环
- asyncio.run_coroutine_threadsafe() 是用于从一个线程向另一个线程中的事件循环提交协程任务的函数
- loop.call_soon_threadsafe(loop.stop) 的作用是:从其他线程安全地通知事件循环:请尽快执行 loop.stop(),停止事件循环
lscpu,thread(s) per core, Core(s) per socket,Socket(s)
CPU(s): 16
Thread(s) per core: 2
Core(s) per socket: 8
Socket(s): 1
| 参数 | 含义 | 例子 |
|---|---|---|
| Socket(s) | CPU 插槽数(物理 CPU 芯片有几块) | 1(主板上只有一块 CPU 芯片) |
| Core(s) per socket | 每块芯片上的物理核心数 | 8(这块 CPU 内有 8 个真正的硬件核心) |
| Thread(s) per core | 每个物理核心的超线程数 | 2(开启了超线程,1 个物理核模拟 2 个逻辑核) |
| CPU(s) | 总逻辑 CPU 数 | 16(操作系统看到的总 CPU 线程数) |
总逻辑 CPU 数 = Socket(s) × Core(s) per socket × Thread(s) per core \text{总逻辑 CPU 数} = \text{Socket(s)} \times \text{Core(s) per socket} \times \text{Thread(s) per core} 总逻辑 CPU 数=Socket(s)×Core(s) per socket×Thread(s) per core

物理核心是真正存在的 CPU 计算单元

物理核心:
是真正的计算并行
超线程:
属于部分并行
什么时候看物理核心?逻辑核心?

物理核心决定真正的并行计算能力;逻辑核心(超线程)是在一个物理核心空闲时,让另一个线程利用剩余资源,提高吞吐量。它不是两个核心轮流执行,而是两个线程共享一个核心的执行资源。

进程和线程是什么?CPU实际执行的是:线程
进程是一个正在运行的程序实例。
线程是进程里面真正执行代码的单位
CPU实际执行的是:
线程
不是进程。










什么是协程?协程是一种运行在线程里的、可以暂停和恢复的任务。事件循环负责在多个协程之间切换
线程是操作系统调度的
线程切换由操作系统决定
协程不是操作系统资源
协程是在一个线程里面运行的
协程之间的切换由程序自己决定
IO密集型:协程优势巨大
CPU密集型:协程没优势
协程
解决:
大量等待任务不要浪费线程
依靠:
一个线程 + 主动让出


为什么叫“可以暂停和恢复”?




怎么判断是不是协程?看有没有 async def
看到:
async def
基本可以判断:
这是一个协程函数
async def 定义出来的函数叫“协程函数(coroutine function)”。调用协程函数以后不会立马执行,而是返回一个“协程对象(coroutine object)”

什么是协程函数?协程函数就是:用 async def 定义的函数

什么是协程对象?一个“还没有执行的任务”。
调用协程函数以后不会立马执行,而是返回一个“协程对象(coroutine object)”


协程对象不能自己执行,必须交给某种“运行器”执行;在 Python 的 asyncio 体系里,通常就是由事件循环(event loop)来执行。事件循环负责调度它、暂停它、恢复它

不是所有带 async 的东西都是协程函数
async with
async with lock:
…
这是异步上下文管理。
不是定义协程。
async for
async for item in stream:
…
这是异步迭代。
不是定义协程。

async with 异步上下文管理器
async with 是 Python 异步上下文管理器(asynchronous context manager),它和普通的 with 很像。
为什么需要 async with?
因为有些资源的获取和释放也是异步的。




import asyncio
class AsyncResource:
async def __aenter__(self):
print("建立连接")
await asyncio.sleep(1)
return self
async def __aexit__(self, exc_type, exc, tb):
print("关闭连接")
await asyncio.sleep(1)
async def work(self):
print("执行任务")
async def main():
async with AsyncResource() as r:
await r.work()
asyncio.run(main())




async with 异步函数 async def 里面使用

async for 每次获取下一个元素时,可能需要等待,所以允许异步等待



import asyncio
class AsyncCounter:
def __init__(self):
self.i = 0
def __aiter__(self):
return self
async def __anext__(self):
if self.i >= 3:
raise StopAsyncIteration
await asyncio.sleep(1)
self.i += 1
return self.i
async def main():
async for x in AsyncCounter():
print(x)
asyncio.run(main())



async for 必须出现在异步上下文中,也就是协程函数(async def)里面。async for 本质就是帮你自动写了:await 下一条数据



协程,线程,进程


多线程 多进程 concurrent.futures.Future
from concurrent.futures import ThreadPoolExecutor
ThreadPoolExecutor,线程池执行器
from concurrent.futures import ProcessPoolExecutor
pool = ProcessPoolExecutor(max_workers=4)

ThreadPoolExecutor(max_workers=x),最多同时运行max_workers个线程







submit,返回concurrent.futures.Future
future = pool.submit(
函数,
参数
)






Future怎么拿结果? concurrent.futures.Future 获取的是“提交给线程池/进程池执行的那个函数的返回结果”






按提交顺序获取结果











谁完成先拿谁” concurrent.futures.as_completed()





多个任务怎么执行?





为什么叫 Pool(池)? 因为线程可以重复利用。


ProcessPoolExecutor(max_workers=4),最多同时执行max_workers个进程


pool.submit(square,10),返回Feature


Feature怎么拿结果 concurrent.futures.Future 获取的是“提交给线程池/进程池执行的那个函数的返回结果”


按提交顺序拿结果

谁先完成先拿谁

多任务并行


ProcessPoolExecutor 叫“进程池”,因为它提前管理一批可重复使用的进程
线程池和进程池中的“池”,就是指创建一批线程/进程放在那里,任务来了直接复用,不需要每次重新创建和销毁






concurrent.futures.Future 是“线程安全”的,因为多个线程可以访问同一个 Future 对象;ProcessPoolExecutor 虽然也返回它,但这个 Future 只存在于主进程,子进程通过 IPC 返回结果,由主进程里的 Future 更新,所以不存在 Future 跨进程共享的问题
线程内存共享,进程内存隔离








IPC 是 Inter-Process Communication(进程间通信) 的缩写。因为进程之间内存隔离,一个进程不能直接访问另一个进程的变量,所以必须通过特殊机制通信
IPC 就是让两个独立进程之间交换数据、发送消息、同步状态的方法。
因为进程之间内存隔离,一个进程不能直接访问另一个进程的变量,所以必须通过特殊机制通信。




Task 可以理解成:Event Loop 管理的“正在执行的协程任务”。协程对象不一定必须手动变成 Task,Event Loop 才能执行。很多情况下,Event Loop 会自动把协程对象包装成 Task
协程对象不一定必须手动变成 Task,Event Loop 才能执行。
很多情况下,Event Loop 会自动把协程对象包装成 Task
1、直接交给 run_until_complete,run_until_complete()-》自动包装成 Task-》 Event Loop执行
2、手动创建 Task,task = asyncio.create_task(协程对象)
3、协程对象直接 await,
async def main():
await hello()
hello()
↓
协程对象
↓
await
↓
Event Loop执行
也没有显式 Task。




谁执行协程?谁负责切换协程?事件循环 Event Loop
协程不是操作系统执行的,而是由线程里的事件循环(Event Loop)负责调度和切换。
真正执行协程代码的,还是某个线程里的 CPU。
负责“什么时候运行哪个协程”的,是 Event Loop。
Event Loop 可以理解为:
一个不断循环运行的“任务调度器”。



Future 是一个“未来结果的占位盒子”。现在没有结果,但是以后会有结果。协程对象描述“要做什么”;Task负责“什么时候做”;Future负责“做完得到什么”。
Future不是任务,它只是“结果等待凭证”。线程池/进程池用 Future 等线程或进程的结果;asyncio 用 Future 等协程的结果。
Future:
不是一个函数。
它表示:
某个异步操作未来会产生结果。
Task 是 Future 的一种特殊形式
Task 是 Future 的子类
Future 自己不知道“怎么做完”,也不知道“做什么”。
Future 只是一个空盒子,别人负责把结果放进去。
它不是预测未来,而是保存未来结果的容器。



创建 Future 只有 loop.create_future() 吗?asyncio 的 Future 必须和某个 Event Loop 关联






future设置结果只有 set_result() 吗?





asyncio.Future属于 asyncio。concurrent.futures.Future它用于线程池、进程池。

asyncio 是什么?Python 官方提供的异步编程框架
先看名字:
asyncio
|
|-- async(异步)
|
|-- io(输入输出)
它是 Python 官方提供的异步编程框架。
它主要提供:
协程(coroutine)
事件循环(Event Loop)
Task
Future
异步 IO
也就是说:
asyncio 是管理协程运行的一整套工具。

什么是事件循环?事件循环(Event Loop)就是一个不断循环运行的调度器,负责检查有哪些任务可以执行,然后安排它们运行。它是 asyncio 异步编程的核心。事件循环绑定并运行在线程中,是运行在线程中的一个调度器,而线程属于某个进程

创建 Event Loop 有几种方式?
1、手动创建:loop = asyncio.new_event_loop(),只是创建,还没运行

asyncio.new_event_loop() 创建 Event Loop 不等于运行


run_forever() 永远运行:启动事件循环,一直循环调度任务,直到有人调用 stop
run_forever没有参数
但是你可能会疑惑:
那它启动 Event Loop 后,运行什么?
答案:
它运行的是已经提交到这个 Event Loop 里的任务(Task / callback / coroutine)。
不是通过参数传进去。


run_until_complete() 运行一个协程,直到它完成,然后退出
loop.run_until_complete(x)
需要一个:
可以等待完成的东西,可以是
1、调用 async def 定义的函数后产生的协程对象
2、Task
3、Feature




方法2:asyncio.run()最常用
asyncio.run(
main(), # 必填:一个协程对象
debug=True # 可选:调试模式
)
asyncio.run() 的参数是 调用 async def函数后返回的协程对象(coroutine object)。


方法3:获取已有 Event Loop asyncio.get_running_loop()

为什么loop = asyncio.get_running_loop()必须在协程里调用?
asyncio.get_running_loop() 必须在协程里调用,是因为它获取的是当前正在运行的 Event Loop,而只有进入协程执行阶段时,Python 才知道“当前线程正在运行哪个 Event Loop”。


loop.stop():让事件循环停止运行。loop.close():关闭事件循环,释放资源,让它不能再使用。








asyncio.run()为什么不用手动close?


loop.stop() 不会等待任务完成,也不会自动取消任务。它只是告诉 Event Loop:“不要再继续循环调度了”








loop.call_soon(loop.stop)
call_soon() 的意思:
不马上执行这个函数,而是把这个函数放到 Event Loop 的任务队列里,等 Event Loop 下一次循环时执行。
call_soon接收的是函数对象(即函数名)






await只能写在哪里?后面可以跟什么?跟不同内容的作用分别是什么 await 到底是什么意思?
await 会暂停当前协程,把后面那个异步对象交给 Event Loop 去推进执行;当前协程等待它完成后,再继续往下执行
await 是协程主动让出执行权的方式。它等待一个异步对象(协程、Task、Future)的结果,但等待期间不会阻塞 Event Loop,而是让 Event Loop 去执行其他任务
遇到 await 时,只是当前协程主动暂停,并告诉 Event Loop:“我要等这个东西,你之后帮我恢复”
await 会暂停当前协程,把后面那个异步对象交给 Event Loop 去推进执行;当前协程等待它完成后,再继续往下执行
await 后面的东西,不一定会被 Event Loop 立即执行。
await 只是建立一个“等待关系”:当前协程暂停,等待这个对象完成。至于这个对象什么时候执行、是否已经在执行,要看它是什么类型(协程对象,task,future)
await 不负责启动后面的对象,它只是让当前协程等待该对象完成;对象是否已经运行、何时运行,由 Event Loop 中对应的调度状态决定
“await等待的那个异步对象完成”,后,Event Loop 会知道,然后自动恢复当前协程,不需要你手动通知。
await Future/Task:等待一个已经存在的异步任务完成;await coroutine:启动并驱动这个协程执行,期间如果遇到等待点则挂起当前协程

“await等待的那个异步对象(协程对象,Task,featur)完成”,后,Event Loop 会知道,然后自动恢复当前协程,不需要你手动通知。







await 后面的东西,不一定会被 Event Loop 立即执行。await 只是建立一个“等待关系”:当前协程暂停,等待这个对象完成。至于这个对象什么时候执行、是否已经在执行,要看它是什么类型(协程对象,task,future).await 后面必须是:Coroutine对象,Task,Future
await 后面必须是:
Coroutine对象
Task
Future
await 协程对象
await后面跟协程对象时,会让当前协程暂停,并建立“等待该协程对象完成”的关系。Event Loop随后负责驱动这个协程对象执行(具体什么时候执行由调度决定),当该协程对象完成后,Event Loop自动恢复await所在的协程
协程对象:创建时没有进入 Event Loop
await 协程对象时
Event Loop 才开始接触这个 coroutine,并负责推进它
await Task
await Task 不负责启动任务,它只是等待一个已经存在的 Task 完成。当前协程等待这个 Task 完成。
创建 Task:
task = asyncio.create_task(worker())
Task 在创建时,就已经被注册到 Event Loop 的调度体系中,等待执行
Task = 已经交给 Event Loop 管理的执行任务

await Future
Future 本身没有要执行的东西。
Future ≠ 要执行的东西
它只是:
一个未来结果的容器
Event Loop知道:
当前协程
正在等待 Future
但是注意:
Event Loop不会说:
Future,你开始执行
因为 Future没有执行逻辑。




await 只能写在 async def 定义的协程函数里面


asyncio.run() 创建 Event Loop → 执行一个协程 → 等协程结束 → 关闭 Event Loop。返回最外层协程的 return 值






asyncio.create_task()。把一个 Coroutine 包装成 Task,并立即提交给当前 Event Loop 调度。返回 Task对象。可await,不await
asyncio.create_task() 创建 Task 后,Task 会进入 Event Loop 调度,是否 await 取决于你是否需要等待它完成、获取结果


asyncio.gather() 同时运行多个协程,并等待它们全部完成,然后返回所有结果。返回future,需要await. await后返回一个列表,里面是每个任务的结果。
gather() 会把多个可等待对象(Coroutine/Task/Future)组合起来,让它们被调度执行,并返回一个代表“全部完成状态”的 Future,方便一次性等待所有结果。
asyncio.gather(coro1,coro2) 会把多个 Coroutine 交给 asyncio 管理(通常转换为 Task),让 Event Loop 可以调度它们,并返回一个 Future,用于等待所有任务完成并收集结果
需要 await 的一个核心原因就是:让当前协程等待这个 Future 完成,并拿到结果。
如果不加 await,当前协程会继续往下执行





asyncio.sleep() 异步睡眠。返回一个协程对象,不会立即执行,需要await
sleep coroutine 也需要被 Event Loop 推进后才会开始计时。因为 await sleep() 通常发生在当前正在运行的 Task 中,所以 Event Loop 很快执行 sleep 的内部逻辑并注册定时器,因此通常认为计时从 await 开始。
“创建协程 ≠ 执行协程;进入 Event Loop 调度 ≠ 已经运行
syncio.sleep
本身是一个:
用 async def 定义的协程函数(coroutine function)
调用它:asyncio.sleep(3)返回
一个协程对象(coroutine object)


什么叫“可等待对象(Awaitable)”? 可等待对象,就是可以被 await 等待的东西

线程 A 不能直接操作线程 B 的事件循环
如果“线程 A”想让“线程 B”里的事件循环帮它运行一个异步函数 init_config_async(),你不能直接在线程 A 里 await,也不能直接调用 self.loop.create_task()(因为线程不安全)
不能直接在线程 A 里 await?await 只能写在 async def 定义的协程函数里面。await 的本质是:当前协程把控制权交给当前线程的事件循环,线程A没有事件循环
事件循环属于线程,事件循环运行在哪个线程,就属于哪个线程


为什么不能self.loop.create_task(init_config_async())?asyncio 规定:Event loop 对象不是线程安全的。



asyncio.run_coroutine_threadsafe() 是用于从一个线程向另一个线程中的事件循环提交协程任务的函数
asyncio 规定:Event loop 对象不是线程安全的。
asyncio.run_coroutine_threadsafe() 是用于从一个线程向另一个线程中的事件循环提交协程任务的函数
concurrent.Future 存放的是这个协程在目标事件循环中执行完成后的:
返回值
异常
取消状态
asyncio.run_coroutine_threadsafe(coro, loop)
它主要用于:
当前线程和事件循环所在的线程不是同一个线程时,安全地把协程提交到那个事件循环

例子
把一个协程 self.init_config_async(config) 提交到指定事件循环 self.loop 中执行,并立即返回一个可在线程间等待结果的 Future
# test_async.py
import asyncio
import threading
import time
class Manager:
def __init__(self):
# 1. 创建一个新的事件循环
self.loop = asyncio.new_event_loop()
# 2. 创建线程运行事件循环
self.thread = threading.Thread(
target=self.run_loop,
daemon=True
)
self.thread.start()
def run_loop(self):
"""
子线程执行这个函数
"""
# 把这个loop设置成当前线程的事件循环
asyncio.set_event_loop(self.loop)
print("事件循环启动")
# 一直运行,等待任务
self.loop.run_forever()
async def init_config_async(self, config):
"""
一个异步任务
"""
print("开始执行异步任务")
# 模拟网络请求
await asyncio.sleep(3)
print("异步任务完成")
return {
"config": config,
"status": "ok"
}
def init_config(self, config):
print("主线程提交任务")
# 把协程提交给另一个线程的事件循环
future = asyncio.run_coroutine_threadsafe(
self.init_config_async(config),
self.loop
)
print("任务已经提交,主线程继续")
# 等待异步任务结果
result = future.result()
print("拿到结果:")
print(result)
manager = Manager()
manager.init_config(
{
"name": "test"
}
)
print("程序结束")
loop.call_soon_threadsafe(loop.stop) 的作用是:从其他线程安全地通知事件循环:请尽快执行 loop.stop(),停止事件循环
call_soon_threadsafe() 参数是普通函数
返回值是asyncio.Handle,拿不到会返回值,因为就不在意返回值。
需要结果使用run_coroutine_threadsafe
loop.stop()
作用:
设置一个标志,让事件循环在当前这一轮任务处理完成后退出













openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

















所有评论(0)