文章目录
Python多任务-协程
同步、异步、阻塞、非阻塞、并行、并发
同步、异步:
- 同步:是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式
- 异步:是指代码调用IO操作时,不必等IO操作完成就返回的调用方式
阻塞、非阻塞:
- 阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
- 非阻塞:从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞
并行、并发
- 并行(多进程):同时做不同事情的能力
- 并发(协程):交替做不同事情的能力
生成器-send方法
send方法有一个参数,该参数指定的是上一次被挂起的yield语句的返回值
def create_num(num):
a, b = 0, 1
current_num = 0
while current_num < num:
result = yield a
print("result-->", result)
a, b = b, a+b
current_num += 1
g = create_num(5)
print(next(g))
# 关闭生成器
# g.close()
# 如果前面不调用next 第一次必须发送None
print(g.send('ying'))
print(g.send('haha'))
-----------结果-------------
0
result--> ying
1
result--> haha
1
使用yield完成多任务
import time
def task1():
while True:
print("--1--")
time.sleep(0.1)
yield
def task2():
while True:
print("--2--")
time.sleep(0.1)
yield
def main():
t1 = task1()
t2 = task2()
while True:
next(t1)
next(t2)
if __name__ == "__main__":
main()
----------------结果-----------------
--1--
--2--
--1--
--2--
--1--
--2--
......
yield from介绍
#python3.3新加了yield from语法
def generator_1():
total = 0
while True:
x = yield
print('加', x)
if not x:
break
total += x
return total
def generator_2(): # 委托生成器
while True:
total = yield from generator_1() # 子生成器
print('加和总数是:', total)
def main(): # 调用方
# g1 = generator_1()
# g1.send(None)
# g1.send(2)
# g1.send(3)
# g1.send(None)
g2 = generator_2()
g2.send(None)
g2.send(2)
g2.send(3)
g2.send(None)
if __name__ == '__main__':
main()
------------结果----------------
加 2
加 3
加 None
加和总数是: 5
【子生成器】:yield from后的generator_1()生成器函数是子生成器
【委托生成器】:generator_2()是程序中的委托生成器,它负责委托子生成器完成具体任务。
【调用方】:main()是程序中的调用方,负责调用委托生成器。
协程
协程,又称微线程
协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)
协程主要是利用程序中的延时时间进行多任务的
Python中的协程大概经历了如下三个阶段:
1. 最初的生成器变形yield/send
2. yield from
3. 在最近的Python3.5版本中引入async/await关键字
使用greenlet完成多任务
安装模块:pip3 install greenlet
# @ Time : 2020/1/17 20:58
# @ Author : JuRan
from greenlet import greenlet
import time
# 协程利用程序的IO 来切换任务
def demo1():
while True:
print("demo1")
gr2.switch()
time.sleep(0.5)
def demo2():
while True:
print("demo2")
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(demo1)
# print(greenlet.__doc__)
gr2 = greenlet(demo2)
gr1.switch()
-----------------------------
demo1
demo2
demo1
demo2
demo1
demo2
......
使用gevent完成多任务
安装模块:pip3 install gevent
import gevent
import time
from gevent import monkey
# 在程序中使用的是time.sleep(0.5),这个在gevent模块内是不生效的
# 使用monkey.patch_all()方法之后就可以生效
monkey.patch_all()
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
#print("--1--")
g1 = gevent.spawn(f1, 5)
#print("--2--")
time.sleep(1)
# gevent.sleep(0.5)
g2 = gevent.spawn(f2, 5)
#print("--3--")
g3 = gevent.spawn(f3, 5)
#print("--4--")
g1.join()
g2.join()
g3.join()
-----------------------------------------
<Greenlet at 0x238daf5bb48: f1(5)> 0
<Greenlet at 0x238daf5bc48: f2(5)> 0
<Greenlet at 0x238daf5bd48: f3(5)> 0
<Greenlet at 0x238daf5bb48: f1(5)> 1
<Greenlet at 0x238daf5bc48: f2(5)> 1
<Greenlet at 0x238daf5bd48: f3(5)> 1
<Greenlet at 0x238daf5bb48: f1(5)> 2
<Greenlet at 0x238daf5bc48: f2(5)> 2
<Greenlet at 0x238daf5bd48: f3(5)> 2
<Greenlet at 0x238daf5bb48: f1(5)> 3
<Greenlet at 0x238daf5bc48: f2(5)> 3
<Greenlet at 0x238daf5bd48: f3(5)> 3
<Greenlet at 0x238daf5bb48: f1(5)> 4
<Greenlet at 0x238daf5bc48: f2(5)> 4
<Greenlet at 0x238daf5bd48: f3(5)> 4
案例
import gevent
from gevent import monkey
monkey.patch_all()
import requests # urllib 进行封装 爬虫 80-90
# 并行 并发
# 协程 并发
def download(url):
print("get: %s" % url)
res = requests.get(url)
data = res.text
print(len(data), url)
# g1 = gevent.spawn(download, 'https://www.baidu.com/')
# g2 = gevent.spawn(download, 'https://www.python.org/')
# g3 = gevent.spawn(download, 'https://www.baidu.com/')
# g1.join()
# g2.join()
# g3.join()
# 可以通过joinall方法,简化代码
gevent.joinall([
gevent.spawn(download, 'https://www.baidu.com/'),
gevent.spawn(download, 'https://www.python.org/'),
gevent.spawn(download, 'https://www.baidu.com/')
])
-------------------------------
get: https://www.baidu.com/
get: https://www.python.org/
get: http://blog.xiaoqying.com/
2443 https://www.baidu.com/
42013 http://blog.xiaoqying.com/
49013 https://www.python.org/
因为python的网站请求比较慢,所以程序会自动先进行其他的操作
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源很最大,效率很低
- 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 协程切换任务资源很小,效率高
- 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
同步、异步:
- 同步:是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式
- 异步:是指代码调用IO操作时,不必等IO操作完成就返回的调用方式
阻塞、非阻塞:
- 阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
- 非阻塞:从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞
并行、并发
- 并行(多进程):同时做不同事情的能力
- 并发(协程):交替做不同事情的能力
send方法有一个参数,该参数指定的是上一次被挂起的yield语句的返回值
def create_num(num):
a, b = 0, 1
current_num = 0
while current_num < num:
result = yield a
print("result-->", result)
a, b = b, a+b
current_num += 1
g = create_num(5)
print(next(g))
# 关闭生成器
# g.close()
# 如果前面不调用next 第一次必须发送None
print(g.send('ying'))
print(g.send('haha'))
-----------结果-------------
0
result--> ying
1
result--> haha
1
使用yield完成多任务
import time
def task1():
while True:
print("--1--")
time.sleep(0.1)
yield
def task2():
while True:
print("--2--")
time.sleep(0.1)
yield
def main():
t1 = task1()
t2 = task2()
while True:
next(t1)
next(t2)
if __name__ == "__main__":
main()
----------------结果-----------------
--1--
--2--
--1--
--2--
--1--
--2--
......
yield from介绍
#python3.3新加了yield from语法
def generator_1():
total = 0
while True:
x = yield
print('加', x)
if not x:
break
total += x
return total
def generator_2(): # 委托生成器
while True:
total = yield from generator_1() # 子生成器
print('加和总数是:', total)
def main(): # 调用方
# g1 = generator_1()
# g1.send(None)
# g1.send(2)
# g1.send(3)
# g1.send(None)
g2 = generator_2()
g2.send(None)
g2.send(2)
g2.send(3)
g2.send(None)
if __name__ == '__main__':
main()
------------结果----------------
加 2
加 3
加 None
加和总数是: 5
【子生成器】:yield from后的generator_1()生成器函数是子生成器
【委托生成器】:generator_2()是程序中的委托生成器,它负责委托子生成器完成具体任务。
【调用方】:main()是程序中的调用方,负责调用委托生成器。
协程
协程,又称微线程
协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)
协程主要是利用程序中的延时时间进行多任务的
Python中的协程大概经历了如下三个阶段:
1. 最初的生成器变形yield/send
2. yield from
3. 在最近的Python3.5版本中引入async/await关键字
使用greenlet完成多任务
安装模块:pip3 install greenlet
# @ Time : 2020/1/17 20:58
# @ Author : JuRan
from greenlet import greenlet
import time
# 协程利用程序的IO 来切换任务
def demo1():
while True:
print("demo1")
gr2.switch()
time.sleep(0.5)
def demo2():
while True:
print("demo2")
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(demo1)
# print(greenlet.__doc__)
gr2 = greenlet(demo2)
gr1.switch()
-----------------------------
demo1
demo2
demo1
demo2
demo1
demo2
......
使用gevent完成多任务
安装模块:pip3 install gevent
import gevent
import time
from gevent import monkey
# 在程序中使用的是time.sleep(0.5),这个在gevent模块内是不生效的
# 使用monkey.patch_all()方法之后就可以生效
monkey.patch_all()
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
#print("--1--")
g1 = gevent.spawn(f1, 5)
#print("--2--")
time.sleep(1)
# gevent.sleep(0.5)
g2 = gevent.spawn(f2, 5)
#print("--3--")
g3 = gevent.spawn(f3, 5)
#print("--4--")
g1.join()
g2.join()
g3.join()
-----------------------------------------
<Greenlet at 0x238daf5bb48: f1(5)> 0
<Greenlet at 0x238daf5bc48: f2(5)> 0
<Greenlet at 0x238daf5bd48: f3(5)> 0
<Greenlet at 0x238daf5bb48: f1(5)> 1
<Greenlet at 0x238daf5bc48: f2(5)> 1
<Greenlet at 0x238daf5bd48: f3(5)> 1
<Greenlet at 0x238daf5bb48: f1(5)> 2
<Greenlet at 0x238daf5bc48: f2(5)> 2
<Greenlet at 0x238daf5bd48: f3(5)> 2
<Greenlet at 0x238daf5bb48: f1(5)> 3
<Greenlet at 0x238daf5bc48: f2(5)> 3
<Greenlet at 0x238daf5bd48: f3(5)> 3
<Greenlet at 0x238daf5bb48: f1(5)> 4
<Greenlet at 0x238daf5bc48: f2(5)> 4
<Greenlet at 0x238daf5bd48: f3(5)> 4
案例
import gevent
from gevent import monkey
monkey.patch_all()
import requests # urllib 进行封装 爬虫 80-90
# 并行 并发
# 协程 并发
def download(url):
print("get: %s" % url)
res = requests.get(url)
data = res.text
print(len(data), url)
# g1 = gevent.spawn(download, 'https://www.baidu.com/')
# g2 = gevent.spawn(download, 'https://www.python.org/')
# g3 = gevent.spawn(download, 'https://www.baidu.com/')
# g1.join()
# g2.join()
# g3.join()
# 可以通过joinall方法,简化代码
gevent.joinall([
gevent.spawn(download, 'https://www.baidu.com/'),
gevent.spawn(download, 'https://www.python.org/'),
gevent.spawn(download, 'https://www.baidu.com/')
])
-------------------------------
get: https://www.baidu.com/
get: https://www.python.org/
get: http://blog.xiaoqying.com/
2443 https://www.baidu.com/
42013 http://blog.xiaoqying.com/
49013 https://www.python.org/
因为python的网站请求比较慢,所以程序会自动先进行其他的操作
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源很最大,效率很低
- 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 协程切换任务资源很小,效率高
- 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
import time
def task1():
while True:
print("--1--")
time.sleep(0.1)
yield
def task2():
while True:
print("--2--")
time.sleep(0.1)
yield
def main():
t1 = task1()
t2 = task2()
while True:
next(t1)
next(t2)
if __name__ == "__main__":
main()
----------------结果-----------------
--1--
--2--
--1--
--2--
--1--
--2--
......
#python3.3新加了yield from语法
def generator_1():
total = 0
while True:
x = yield
print('加', x)
if not x:
break
total += x
return total
def generator_2(): # 委托生成器
while True:
total = yield from generator_1() # 子生成器
print('加和总数是:', total)
def main(): # 调用方
# g1 = generator_1()
# g1.send(None)
# g1.send(2)
# g1.send(3)
# g1.send(None)
g2 = generator_2()
g2.send(None)
g2.send(2)
g2.send(3)
g2.send(None)
if __name__ == '__main__':
main()
------------结果----------------
加 2
加 3
加 None
加和总数是: 5
【子生成器】:yield from后的generator_1()生成器函数是子生成器
【委托生成器】:generator_2()是程序中的委托生成器,它负责委托子生成器完成具体任务。
【调用方】:main()是程序中的调用方,负责调用委托生成器。
协程
协程,又称微线程
协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)
协程主要是利用程序中的延时时间进行多任务的
Python中的协程大概经历了如下三个阶段:
1. 最初的生成器变形yield/send
2. yield from
3. 在最近的Python3.5版本中引入async/await关键字
使用greenlet完成多任务
安装模块:pip3 install greenlet
# @ Time : 2020/1/17 20:58
# @ Author : JuRan
from greenlet import greenlet
import time
# 协程利用程序的IO 来切换任务
def demo1():
while True:
print("demo1")
gr2.switch()
time.sleep(0.5)
def demo2():
while True:
print("demo2")
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(demo1)
# print(greenlet.__doc__)
gr2 = greenlet(demo2)
gr1.switch()
-----------------------------
demo1
demo2
demo1
demo2
demo1
demo2
......
使用gevent完成多任务
安装模块:pip3 install gevent
import gevent
import time
from gevent import monkey
# 在程序中使用的是time.sleep(0.5),这个在gevent模块内是不生效的
# 使用monkey.patch_all()方法之后就可以生效
monkey.patch_all()
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
#print("--1--")
g1 = gevent.spawn(f1, 5)
#print("--2--")
time.sleep(1)
# gevent.sleep(0.5)
g2 = gevent.spawn(f2, 5)
#print("--3--")
g3 = gevent.spawn(f3, 5)
#print("--4--")
g1.join()
g2.join()
g3.join()
-----------------------------------------
<Greenlet at 0x238daf5bb48: f1(5)> 0
<Greenlet at 0x238daf5bc48: f2(5)> 0
<Greenlet at 0x238daf5bd48: f3(5)> 0
<Greenlet at 0x238daf5bb48: f1(5)> 1
<Greenlet at 0x238daf5bc48: f2(5)> 1
<Greenlet at 0x238daf5bd48: f3(5)> 1
<Greenlet at 0x238daf5bb48: f1(5)> 2
<Greenlet at 0x238daf5bc48: f2(5)> 2
<Greenlet at 0x238daf5bd48: f3(5)> 2
<Greenlet at 0x238daf5bb48: f1(5)> 3
<Greenlet at 0x238daf5bc48: f2(5)> 3
<Greenlet at 0x238daf5bd48: f3(5)> 3
<Greenlet at 0x238daf5bb48: f1(5)> 4
<Greenlet at 0x238daf5bc48: f2(5)> 4
<Greenlet at 0x238daf5bd48: f3(5)> 4
案例
import gevent
from gevent import monkey
monkey.patch_all()
import requests # urllib 进行封装 爬虫 80-90
# 并行 并发
# 协程 并发
def download(url):
print("get: %s" % url)
res = requests.get(url)
data = res.text
print(len(data), url)
# g1 = gevent.spawn(download, 'https://www.baidu.com/')
# g2 = gevent.spawn(download, 'https://www.python.org/')
# g3 = gevent.spawn(download, 'https://www.baidu.com/')
# g1.join()
# g2.join()
# g3.join()
# 可以通过joinall方法,简化代码
gevent.joinall([
gevent.spawn(download, 'https://www.baidu.com/'),
gevent.spawn(download, 'https://www.python.org/'),
gevent.spawn(download, 'https://www.baidu.com/')
])
-------------------------------
get: https://www.baidu.com/
get: https://www.python.org/
get: http://blog.xiaoqying.com/
2443 https://www.baidu.com/
42013 http://blog.xiaoqying.com/
49013 https://www.python.org/
因为python的网站请求比较慢,所以程序会自动先进行其他的操作
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源很最大,效率很低
- 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 协程切换任务资源很小,效率高
- 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
安装模块:pip3 install greenlet
# @ Time : 2020/1/17 20:58
# @ Author : JuRan
from greenlet import greenlet
import time
# 协程利用程序的IO 来切换任务
def demo1():
while True:
print("demo1")
gr2.switch()
time.sleep(0.5)
def demo2():
while True:
print("demo2")
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(demo1)
# print(greenlet.__doc__)
gr2 = greenlet(demo2)
gr1.switch()
-----------------------------
demo1
demo2
demo1
demo2
demo1
demo2
......
安装模块:pip3 install gevent
import gevent
import time
from gevent import monkey
# 在程序中使用的是time.sleep(0.5),这个在gevent模块内是不生效的
# 使用monkey.patch_all()方法之后就可以生效
monkey.patch_all()
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5)
# gevent.sleep(0.5)
#print("--1--")
g1 = gevent.spawn(f1, 5)
#print("--2--")
time.sleep(1)
# gevent.sleep(0.5)
g2 = gevent.spawn(f2, 5)
#print("--3--")
g3 = gevent.spawn(f3, 5)
#print("--4--")
g1.join()
g2.join()
g3.join()
-----------------------------------------
<Greenlet at 0x238daf5bb48: f1(5)> 0
<Greenlet at 0x238daf5bc48: f2(5)> 0
<Greenlet at 0x238daf5bd48: f3(5)> 0
<Greenlet at 0x238daf5bb48: f1(5)> 1
<Greenlet at 0x238daf5bc48: f2(5)> 1
<Greenlet at 0x238daf5bd48: f3(5)> 1
<Greenlet at 0x238daf5bb48: f1(5)> 2
<Greenlet at 0x238daf5bc48: f2(5)> 2
<Greenlet at 0x238daf5bd48: f3(5)> 2
<Greenlet at 0x238daf5bb48: f1(5)> 3
<Greenlet at 0x238daf5bc48: f2(5)> 3
<Greenlet at 0x238daf5bd48: f3(5)> 3
<Greenlet at 0x238daf5bb48: f1(5)> 4
<Greenlet at 0x238daf5bc48: f2(5)> 4
<Greenlet at 0x238daf5bd48: f3(5)> 4
案例
import gevent
from gevent import monkey
monkey.patch_all()
import requests # urllib 进行封装 爬虫 80-90
# 并行 并发
# 协程 并发
def download(url):
print("get: %s" % url)
res = requests.get(url)
data = res.text
print(len(data), url)
# g1 = gevent.spawn(download, 'https://www.baidu.com/')
# g2 = gevent.spawn(download, 'https://www.python.org/')
# g3 = gevent.spawn(download, 'https://www.baidu.com/')
# g1.join()
# g2.join()
# g3.join()
# 可以通过joinall方法,简化代码
gevent.joinall([
gevent.spawn(download, 'https://www.baidu.com/'),
gevent.spawn(download, 'https://www.python.org/'),
gevent.spawn(download, 'https://www.baidu.com/')
])
-------------------------------
get: https://www.baidu.com/
get: https://www.python.org/
get: http://blog.xiaoqying.com/
2443 https://www.baidu.com/
42013 http://blog.xiaoqying.com/
49013 https://www.python.org/
因为python的网站请求比较慢,所以程序会自动先进行其他的操作
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源很最大,效率很低
- 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 协程切换任务资源很小,效率高
- 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发