zoukankan      html  css  js  c++  java
  • Python(协程)

    day32

    协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程

    协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

    协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

    复习yield

     1 def f():
     2     print('ok1')
     3     count = yield 5
     4     print('count:', count)
     5     print('ok2')
     6     yield 67
     7 
     8 gen = f()#生成器对象
     9 
    10 ret1 = next(gen)#返回5    等同ret1 = gen.send(None)
    11 print(ret1)
    12 #从count = yield处开始执行
    13 ret2 = gen.send(10)#返回67
    14 print(ret2)

    第十行执行到yield = 5,返回一个5。

    下一次从count = yeild开始执行。

    ok1
    5
    count: 10
    ok2
    67
    
    Process finished with exit code 0

    yield协程并发

     1 def consumer(name):
     2     print("--->starting eating baozi...")
     3     while True:
     4         new_baozi = yield
     5         print("[%s] is eating baozi %s" % (name, new_baozi))
     6         # time.sleep(1)
     7 
     8 def producer():
     9     next(con)
    10     next(con2)
    11     n = 0
    12     while n < 5:
    13         n += 1
    14         con.send(n)
    15         con2.send(n)
    16         print("33[32;1m[producer]33[0m is making baozi %s" % n)
    17 
    18 if __name__ == '__main__':
    19     con = consumer("c1")#创建一个生成器对象, 其中有yield
    20     con2 = consumer("c2")
    21     p = producer()

    可以在19行处设断点调试。

    从con,con2中选择con分析程序的执行过程。

    next(con)进入到consumer函数,执行到new_baozi = yield阻断,new_baozi = yeild并未执行,且无返回值。

    到while循环的con.send(n),使new_baozi = n,并且print,即第9行。

    由于是while循环,循环到new_baozi = yield处继续阻断,等下一次for循环的con.send(n)。

    producer中send完,consumer便会立即输出,与协程相类似。

    我们先给协程一个标准定义,即符合什么条件就能称之为协程:

    1. 必须在只有一个单线程里实现并发
    2. 修改共享数据不需加锁
    3. 用户程序里自己保存多个控制流的上下文栈
    4. 一个协程遇到IO操作自动切换到其它协程

    基于上面这4点定义,我们刚才用yield实现的程并不能算是合格的线程,因为它有一点功能没实现,哪一点呢?

    执行结果:

    --->starting eating baozi...
    --->starting eating baozi...
    [c1] is eating baozi 1
    [c2] is eating baozi 1
    [producer] is making baozi 1
    [c1] is eating baozi 2
    [c2] is eating baozi 2
    [producer] is making baozi 2
    [c1] is eating baozi 3
    [c2] is eating baozi 3
    [producer] is making baozi 3
    [c1] is eating baozi 4
    [c2] is eating baozi 4
    [producer] is making baozi 4
    [c1] is eating baozi 5
    [c2] is eating baozi 5
    [producer] is making baozi 5
    
    Process finished with exit code 0

    greenlet

    完成不同任务间的切换。

     1 # -*- coding:utf-8 -*-
     2 from greenlet import greenlet
     3 
     4 def test1():
     5     print(12)
     6     gr2.switch()
     7     print(34)
     8     gr2.switch()
     9 
    10 def test2():
    11     print(56)
    12     gr1.switch()
    13     print(78)
    14 
    15 gr1 = greenlet(test1)
    16 gr2 = greenlet(test2)
    17 gr1.switch()#先执行test1函数,遇到gr2.switch(),到test2中,从之前停止处开始执行
    18 #完成不同任务间的切换

    执行结果:

    12
    56
    34
    78
    
    Process finished with exit code 0

    gevent

     1 import gevent
     2 import time
     3 
     4 def func1():
     5     print('33[31;1m a33[0m', time.ctime())
     6     gevent.sleep(2)#模拟IO阻塞的情况,阻塞时执行func2中
     7     print('33[31;1m b33[0m', time.ctime())
     8 
     9 
    10 def func2():
    11     print('33[32;1m c33[0m', time.ctime())
    12     gevent.sleep(1)#不是time.sleep()
    13     print('33[32;1m d33[0m', time.ctime())
    14 
    15 
    16 gevent.joinall([
    17     gevent.spawn(func1),
    18     gevent.spawn(func2),
    19     # gevent.spawn(func3),
    20 ])

    先执行func1,到gevent.sleep(2)时,模拟了IO阻塞,马上执行func2,当func2中的阻塞1秒后,func1还处于阻塞状态,func2继续执行。

    执行结果:

     a Tue Nov  6 16:37:04 2018
     c Tue Nov  6 16:37:04 2018
     d Tue Nov  6 16:37:05 2018
     b Tue Nov  6 16:37:06 2018
    
    Process finished with exit code 0

    协程实战

     1 from gevent import monkey
     2 monkey.patch_all()#可以检测到阻塞
     3 
     4 import gevent
     5 from urllib.request import urlopen
     6 import time
     7 
     8 def f(url):
     9     print('GET: %s' % url)
    10     resp = urlopen(url)
    11     data = resp.read()
    12 
    13     with open('xiaohua.html', 'wb') as f:
    14         f.write(data)
    15 
    16     print('%d bytes received from %s.' % (len(data), url))
    17 
    18                                    # f('http://www.xiaohuar.com/')
    19 a = time.time()
    20 gevent.joinall([
    21     gevent.spawn(f, 'https://www.python.org/'),
    22     gevent.spawn(f, 'https://www.yahoo.com/'),
    23     gevent.spawn(f, 'https://github.com/'),
    24 ])
    25 
    26 b = time.time()
    27 
    28 # l = ['https://www.python.org/', 'https://www.yahoo.com/', 'https://github.com/']
    29 # for url in l:
    30 #     f(url)
    31 # c = time.time()
    32 
    33 # print('普通方法所需时间:', c - b)#10s
    34 
    35 print('使用协程所需时间:', b - a)# 2s

    爬虫使用协程,爬取网络更快。遇到阻塞会更换任务。

    而且monkey可以检测到是否阻塞。

    执行结果:

    GET: https://www.python.org/
    GET: https://www.yahoo.com/
    GET: https://github.com/
    65038 bytes received from https://github.com/.
    48925 bytes received from https://www.python.org/.
    517942 bytes received from https://www.yahoo.com/.
    使用协程所需时间: 5.216813802719116
    
    Process finished with exit code 0
  • 相关阅读:
    我非要捅穿这 Neutron(三)架构分析与代码实现篇(基于 OpenStack Rocky)
    我非要捅穿这 Neutron(二)上层资源模型篇
    $('.one + div')选取class为one的下一个元素
    15分钟,教你用Python爬网站数据,并用BI可视化分析!
    $("div span")选取里的所有的元素
    根据给定的元素名匹配元素
    根据给定的类名匹配元素
    根据给定的id匹配一个元素
    想创业,请问有没有投资小的项目?
    Vue组件间的通信
  • 原文地址:https://www.cnblogs.com/112358nizhipeng/p/9915055.html
Copyright © 2011-2022 走看看