Skip to content

Commit a812bf6

Browse files
committed
关于协程的更新, 有一些资料缺失
1 parent 494af06 commit a812bf6

14 files changed

+483
-59
lines changed

01 Python 基础/Python 05 作用域, 函数, 模块, 迭代器和生成器.txt

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,28 +189,42 @@ yield语句
189189
yield 用来生成数据,供迭代器的next(iteration)函数使用
190190
注: yield函数由Python提供
191191
示例:
192-
def myyeild(): # 生成器函数, function object
193-
print("yield函数被调用")
192+
def go_yield(): # 生成器函数, 调用返回一个生成器
193+
print("生成器函数被调用") # 仅在第一次时执行
194194
yield 2
195195
yield 3
196196
yield 5
197197
yield 6
198198

199-
myyeild() # 调用, myyield函数将返回一个可迭代对象(生成器对象)
200-
gen = myyeild() # gen绑定生成器对象
199+
def go_yield2(): # 生成器函数, 调用返回一个生成器
200+
count = 0
201+
print(f"{go_yield2.__name__}生成器被调用")
202+
while True: # 一个无限可生成计数的生成器
203+
count += 1
204+
yield count
205+
206+
go_yield() # 调用, go_yield函数将返回一个可迭代对象(生成器对象)
207+
gen = go_yield() # 再次调用返回一个生成器对象, 用gen绑定该生成器对象
201208
it = iter(gen) # it绑定迭代器对象
202209
print(next(it)) # 2
203210
print(next(it)) # 3
204211
print(next(it)) # 5
205212
print(next(it)) # 6
206-
print(next(it)) # StopIteration异常
213+
# print(next(it)) # StopIteration异常
207214

208-
for x in myyeild(): # 用for访问生成器函数
215+
for x in go_yield(): # 用for访问生成器
216+
print(x)
217+
218+
gen2 = go_yield2()
219+
for x in gen2:
220+
if x>10:
221+
break
209222
print(x)
210223

211224
生成器函数说明:
212225
生成器函数的调用将返回一个生成器对象,生成器对象是一个可迭代对象
213226
生成器函数调用return语句会触发一个StopIteration异常
227+
每一次运行到yield语句的时候, 生成器将会中断且保留此次的数据
214228

215229
生成器函数的应用示例:
216230
def myinteger(begin, end):
@@ -223,6 +237,8 @@ def myinteger(begin, end):
223237
for x in myinteger(10, 20):
224238
print(x)
225239

240+
补充:
241+
利用生成器在执行完yield时中断且保留此时数据的特点, Python以此扩展出了基于任务循环的协程
226242

227243
生成器表达式
228244
语法:

04 多进程,多线程,协程/01 多任务编程基础.txt

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,40 @@
22

33
通过应用程序利用多个计算机核心达到多任务同时执行的目的,以此来提升程序的执行效率
44

5-
并发: 同时处理多个请求, 但是内核采用轮询时间片的方式逐个访问, 某一时间点实际只处理一个任务
6-
IO多路复用, 协程, 循环服务器 等都是单线程
7-
并行: 使用多个内核同时执行多个任务
5+
6+
---------------------------------------
7+
8+
并发Concurrency 和 并行Parallelism
9+
10+
并发
11+
并发是指系统能够处理多个任务的能力,但这些任务可能并不是同时进行的。对于多个任务, 内核采用轮询时间片的方式逐个访问, 某一时间点实际只处理一个任务.
12+
在并发执行中,单个处理器通过快速切换任务的方式给人一种"同时处理多个任务"的错觉。实际上,这些任务在不同的时间片段内交替执行。
13+
如: 大数据处理
14+
15+
并行
16+
并行是指多个任务或多个部分同时在多个处理器上执行。即使用多个内核同时执行多个任务.
17+
并行是并发的子集,只有在多核或多处理器环境中才能实现真正的并行执行。
18+
如: I/O密集型应用
19+
20+
实现并发与并行的方式
821
多进程, 多线程
922

10-
实现方式: 多进程, 多线程
11-
进程 : 程序在计算机中一次执行的过程
23+
进程
24+
程序在计算机中一次执行的过程
1225

13-
程序 : 是一个静态的描述, 不占有计算机资源
14-
进程 : 是一个动态的描述, 占有CPU内存的计算机资源, 是CPU管理资源的最小单位
26+
区分程序与进程
27+
程序是一个静态的描述, 不占有计算机资源
28+
进程是一个动态的描述, 占有CPU内存的计算机资源, 是CPU管理资源的最小单位
1529

1630
注意:
1731
同一个程序, 每次执行都是不同的进程, 因为分配的计算机资源不同
1832

1933

34+
---------------------------------------
35+
36+
进程 Process
37+
进程是CPU资源分配的最小单位, 也是操作系统进行资源分配和调度运行的基本单位
38+
2039
1.进程的创建流程
2140
用户空间运行程序发起进程创建申请 --> 调用OS内核接口创建进程 --> 分配计算机资源, 确定进程状态 --> 将新的进程提供给用户使用
2241

@@ -28,8 +47,8 @@
2847
3.进程有哪些信息, 如何保存
2948
PCB(Process Control Block, 进程控制块) : 在linux或unix系统中进程创建后, 会在内存开辟一块空间存放进程的相关信息, 称为PCB
3049

31-
查看进程信息 ps -aux
32-
信息 : 用户 PID 占有内存 优先级 等
50+
查看此时系统中所有进程信息 ps -aux
51+
表头 : 用户 PID 占有内存 优先级 等
3352
PID : 在操作系统中进程中的唯一标志, 是大于0的整数, 由系统自动分配
3453

3554
4.进程特征
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
协程 Coroutine
2+
协程或又叫微线程, 纤程, 协程本质是在一个线程内, 通过轮询的方式执行不同的任务.
3+
4+
工作原理
5+
对比于进程与线程是系统CPU的能力, 协程是由程序员通过代码实现的, 其建立在系统的线程之上.
6+
通过应用层记录程序的上下文栈区, 实现程序运行中的跳跃. 进而实现选择代码段执行.
7+
8+
一般一个线程栈大小为1MB, 使用多线程, 在高并发下, cpu大部分的时间都将用于切换线程上下文.
9+
由于线程的切换是在内核完成的, 会耗费额外的空间和时间. 而且由于内存都分配给线程栈了, 将频繁地进行内存置换算法, 浪费了很多cpu时间片.
10+
11+
协程, 可以理解为一种在线程里跑的子线程, 它的默认栈空间很小 (比如go的协程栈默认大小为2KB).
12+
当多个协程在一个线程上运行时, 协程间会切换着运行, 协程的切换完全在用户态完成, 而且时机由程序员来自行调度, 从而使得线程的并发量大大提升.
13+
14+
但要注意的是协程只适用于IO密集型程序(大部分时间在等待的), 对于计算密集型程序, 协程的优势并不大, 因为没有给它切换的时间, cpu大部分时间都在工作.
15+
16+
常见的IO密集型程序有 网络请求、文件读写、数据库查询等,
17+
18+
优点:
19+
无需多线程切换的开销
20+
资源消耗非常少
21+
无需进行同步互斥操作
22+
对IO操作并发性极好
23+
缺点:
24+
无法利用计算机多核资源
25+
如果程序阻塞会阻塞整个程序运行
26+
27+
28+
异步IO
29+
同步: 先执行第一个事务, 如果遇到阻塞, 则进行等待直到第一个事务执行完毕, 再执行第二个任务.
30+
异步: 执行第一个事务后, 如果遇到阻塞, 则会执行第二个事务, 不会等待. 可以通过状态, 通知, 回调来调用处理结果.
31+
32+
33+
---------------------------------------
34+
35+
Python中协程的发展
36+
37+
1) python实现协程基本原理 yield语句
38+
示例
39+
import time
40+
41+
def fun_a():
42+
while True:
43+
print(f"{fun_a.__name__}调用")
44+
yield
45+
time.sleep(1)
46+
47+
def fun_b(gen):
48+
while True:
49+
print(f"{fun_b.__name__}调用")
50+
gen.__next__() # next(gen)
51+
52+
# a 为fun_a函数的生成器, 将其给fun_b函数
53+
# fun_b函数将先执行, 打印fun_b调用, 然后获取生成器中的一个对象, 该对象调用, 将打印fun_a调用
54+
# 以上, 将会不断的交替执行, 这就是任务的切换
55+
a = fun_a()
56+
fun_b(a)
57+
58+
59+
2) 在最早的Python 3.4中,协程函数是通过 @asyncio.coroutine 和 yeild from 实现的, 如:
60+
61+
import asyncio
62+
63+
@asyncio.coroutine
64+
def func1(i):
65+
print("协程函数{}马上开始执行。".format(i))
66+
yield from asyncio.sleep(2)
67+
print("协程函数{}执行完毕!".format(i))
68+
69+
if __name__ == '__main__':
70+
# 获取事件循环
71+
loop = asyncio.get_event_loop()
72+
73+
# 执行协程任务, 直到完成
74+
loop.run_until_complete(func1(1))
75+
76+
# 关闭事件循环
77+
loop.close()
78+
79+
80+
2) asyncio / await 语法的引入
81+
82+
Python 3.5以后引入了async/await 语法定义协程函数, 如
83+
84+
async def func1(i):
85+
print("协程函数{}马上开始执行。".format(i))
86+
await asyncio.sleep(2)
87+
print("协程函数{}执行完毕!".format(i))
88+
89+
通过 async/await 语法来声明 协程 是编写 asyncio 应用的推荐方式。
90+
91+
92+
3) 更简洁的执行协程对象
93+
根据协程在Python中的实现, 执行协程任务, 需要三步, 即
94+
(1) 获取事件循环
95+
(2) 将协程对象注册到事件循环中执行
96+
(3) 关闭事件按循环
97+
Python 3.7提供了一个更简便的asyncio.run方法,以便能够更快速的执行一个协程, 如
98+
asyncio.fun(func(1))
99+
100+
执行协程函数只会得到协程对象,不会立刻执行函数内的代码。 如
101+
<coroutine object func at 0x1053bb7c8>
102+
103+
每个协程函数都以async声明,以区别于普通函数,对于耗时的代码或函数使用await声明,表示碰到等待时挂起,以切换到其它任务。
104+
105+
106+
---------------------------------------
107+
108+
asyncio 标准库
109+
110+
1) asyncio中的基本概念
111+
112+
asyncio 的编程模型本质是一个消息循环
113+
一般先定义一个协程函数(或任务), 从 asyncio 模块中获取事件循环loop.
114+
然后把需要执行的协程任务(或任务列表)扔到事件loop中执行,就实现了异步IO.
115+
116+
event_loop事件循环: 程序开启一个无限的循环, 程序员可以将一些函数注册到事件循环上, 当满足事件发生的时候, 调用相应的协程函数.
117+
事件循环会对当前存在的任务进行状态的判断
118+
如, 已完成, 未完成
119+
对已完成的任务会进行删除, 如果未完成的任务/等待执行的任务会被事件循环调度
120+
如果当前事件循环中的任务已经全部完成, 则事件循环列表为空, 会中断循环并退出
121+
122+
coroutine协程: 协程对象, 指一个使用async关键字定义的函数, 该函数的调用不会立即执行函数, 而是会返回一个协程对象.
123+
协程对象需要注册到事件循环, 由事件循环调用.
124+
125+
task任务: 一个协程对象就是一个原生可以挂起的函数, 任务则是对协程进一步的封装, 其中包含任务的各种状态.
126+
127+
future: 代表将来执行或者没有执行的任务的结果, 他和task没有本质上的区别
128+
129+
协程: 通过async 关键字装饰的函数是一个协程函数, 调用该函数会返回一个协程对象
130+
131+
async 关键字: async定义一个协程.
132+
格式: async def 函数名(参数): pass
133+
134+
await 关键字: 遇到阻塞后,先挂起当前协程(任务),让事件循环去执行其他任务(如果有的话),等待“可等待对象”执行完成后,再继续执行下面的代码。
135+
格式: await 可等待对象
136+
获取可等待对象的返回值
137+
格式: 变量名 = await 可等待对象
138+
139+
可等待对象
140+
可等待对象是指可以在await语句中使用的对象,它主要有三种:协程对象、Task对象和 Future对象
141+
142+
2) 事件循环
143+
144+
2) 运行协程的三种主要方式
145+
(1) 用asyncio.run()函数用来运行.
146+
(2) 使用await关键字"等待"一个协程对象.
147+
(3) asyncio.create_task()函数用来并发运行作为 asyncio 任务的多个协程。
148+
149+
150+
151+
152+
补充
153+
inspect 模块来检查函数的类型。特别是,您可以使用 inspect 模块的 isasyncgenfunction() 和 iscoroutinefunction() 函数来检查函数是否是异步生成器函数或协程函数。
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,26 @@
1-
协程
2-
3-
并发编程: 多进程 多线程 IO多路复用
4-
5-
定义: 纤程或又叫微线程, 协程本质实际只有一个线程在运行
6-
7-
工作原理: 通过应用层记录程序的上下文栈区, 实现程序运行中的跳跃. 进而实现选择代码段执行.
8-
9-
优点:
10-
无需多线程切换的开销
11-
资源消耗非常少
12-
无需进行同步互斥操作
13-
对IO操作并发性极好
14-
缺点:
15-
无法利用计算机多核资源
16-
如果程序阻塞会阻塞整个程序运行
17-
18-
python实现协程基本原理 yield语句
19-
20-
使用第三方协程包 greenlet
21-
22-
对greenlet进行包装的协程第三方库 gevent
23-
gevent.spawn(func, argv)
24-
功能: 将func变为协程事件并启动
25-
参数: func 事件函数
26-
argv 多项, 为func的参数
27-
返回值: 协程对象
28-
29-
gevent.join()
30-
功能: 回收协程
31-
参数: 要回收的协程对象
32-
33-
gevent.joinall()
34-
功能: 回收协程
35-
参数: 列表, 里面可以有多个协程对象
36-
37-
gevent.sleep()
38-
功能: 模拟IO阻塞的情况
39-
参数: n 睡眠秒数
40-
41-
from gevent import monkey 库中的插件
42-
monkey.path_all()
43-
功能: 在导入socket模块前使用, 可以将socket模块IO设置为非阻塞
44-
1+
使用第三方协程包 greenlet
2+
3+
对greenlet进行包装的协程第三方库 gevent
4+
gevent.spawn(func, argv)
5+
功能: 将func变为协程事件并启动
6+
参数: func 事件函数
7+
argv 多项, 为func的参数
8+
返回值: 协程对象
9+
10+
gevent.join()
11+
功能: 回收协程
12+
参数: 要回收的协程对象
13+
14+
gevent.joinall()
15+
功能: 回收协程
16+
参数: 列表, 里面可以有多个协程对象
17+
18+
gevent.sleep()
19+
功能: 模拟IO阻塞的情况
20+
参数: n 睡眠秒数
21+
22+
from gevent import monkey 库中的插件
23+
monkey.path_all()
24+
功能: 在导入socket模块前使用, 可以将socket模块IO设置为非阻塞
25+
26+

0 commit comments

Comments
 (0)