当前位置:   article > 正文

Python高级编程技巧第8讲 - Python多任务 - 协程

Python高级编程技巧第8讲 - Python多任务 - 协程

一、协程–并发(单线程完成多任务)

同步、异步

  • 同步:是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式
  • 异步:是指代码调用IO操作时,不必等IO操作完成就返回的调用方式

阻塞、非阻塞

  • 阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
  • 非阻塞:从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞

1.1 生成器-send方法

启动生成器: g.send(None) 或者 next(g), 如果不调用next 第一次必须send(None)
关闭生成器: close()

def create_num(num):
    a, b = 0, 1

    current_num = 0

    while current_num < num:
        res = yield a
        print('res-->', res)
        a, b = b, a+b
        current_num += 1

    # return 'yxh'

g = create_num(5)
# 启动生成器  g.send(None) 或者 next(g), 如果不调用next 第一次必须send(None)
print(next(g))

print(g.send('yxh'))
print(g.send('sdad'))
# 关闭生成器
g.close()
"""
结果: 0
      res--> yxh
      1
      res--> sdad
      1
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在这里插入图片描述

1.2 使用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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在这里插入图片描述

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()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
结果:加 2
	 加 3
	 加 None
	加和总数是: 5
  • 1
  • 2
  • 3
  • 4

【子生成器】:yield from后的generator_1()生成器函数是子生成器
【委托生成器】:generator_2()是程序中的委托生成器,它负责委托子生成器完成具体任务。
【调用方】:main()是程序中的调用方,负责调用委托生成器。

1.3 greenlet实现多任务


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)
gr2 = greenlet(demo2)

gr1.switch()
gr2.switch()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

1.4 gevent实现多任务

import gevent
import time
from gevent import monkey
# 将程序中用到的耗时操作  换为gevent中实现的模块
monkey.patch_all()

"""
gevent.getcurrent()  获取当前gevent对象
代码重构 可以 from gevent import monkey  的  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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

1.6 gevent简单案例–下载器

import gevent

from gevent import monkey
monkey.patch_all()
# requests放在 monkey.patch_all()后面导入不会有报警
import requests    # urllib 进行封装


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()


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:https://www.baidu.com/
2443 https://www.baidu.com/
2443 https://www.baidu.com/
48794 https://www.python.org/
get:https://www.baidu.com/
get:https://www.python.org/
get:https://www.baidu.com/
2443 https://www.baidu.com/
2443 https://www.baidu.com/
48794 https://www.python.org/
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

总结

  • 进程是资源分配的单位
  • 线程是操作系统调度的单位
  • 进程切换需要的资源很最大,效率很低
  • 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
  • 协程切换任务资源很小,效率高
  • 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/768954
推荐阅读
相关标签
  

闽ICP备14008679号