第三十九次总结:协程

函数的复习

函数的定义与调用

在不执行代码的情况下

需要明确代码是如何一步一步执行的

def func():
    print(1)
    return 2
    print(2)
​
​
res = func()
print(res)

 

 

生成器函数复习

把return换为yield

函数就会变为一个生成器

只要函数中有了yield,那么这个函数就是生成器函数

 

 

对象的创建 = 类名()

生成器的创建 = 生成器函数名()

 

希望让生成器对象执行起来

生成器对象.__next__()

 

生成器对象运行起来后

它会执行函数内部的代码

直到遇见yield就会停住

返回yield后面的数据

并且指针状态会停留在yield这一行的行未

 

 

当第二次执行

生成器对象.__next__()

从上一次停留的地方继续往下走

情况1,直到遇见yield就会停住,返回yield后面的数据

情况2,没有遇到yield,函数结束,它会按照迭代器协议报错。得不到数据,经协过的代码仍会被执行.

 

测试代码

def func():
    print(1)
    yield 2
    print(22)
​
​
res = func()
print(res)
n = res.__next__()
print(n)
​
n = res.__next__()
print(n)
​
​
# for i in res:
#     print(i)

 

另一种方式让生成器函数跑起来

方式一

生成器对象.__next__()

方式二

next(生成器对象)

 

def func():
    print(1)
    yield 2
    print(22)
​
​
res = func()
n = next(res)
print(n)
​
n = next(res)
print(n)

 

 

generator 英 [ˈdʒenəreɪtə(r)] 美 [ˈdʒenəreɪtər] n. 发电机;发生器;电力公司

 

iter函数

下面两种情况是一样的效果

iter_obj = res.__iter__()
iter2_obj = iter(res)

 

效果有点像

li = [1,2,3]
​
li.sort()
sorted(li)

 

协程

生成器的进阶

复习函数生成器的特点

关注函数内的代码什么时候会被执行

 

使用yield协程计算大量的数据

由于两个生成器对象是切换占用cpu,执行任务

不会发生同一时刻抢的情况

因此不用加锁,不存在数据的混乱

当前执行,使用了一个进程,一个主线程来完成的

 

 

 

 

 

 

import time
​
isum = 0
​
def addnum():
    global isum
    for i in range(10000000):
        isum += 1
        yield isum
​
g1 = addnum()
g2 = addnum()
​
start_time = time.time()
for i in range(10000000):
    next(g1)
    next(g2)
​
print(isum)
end_time = time.time()
cost_time = end_time - start_time
print('花费了%.f秒' % cost_time)

 

 

对比通过多线程来计算,不加锁的情况

结果,多线程完败协程

多线程执行同样代码,耗时11秒,协程砂时4秒

 

 

 

 

 

 

import time
​
isum = 0
​
def addnum():
    global isum
    for i in range(10000000):
        isum += 1
        yield isum
​
g1 = addnum()
g2 = addnum()
​
start_time = time.time()
for i in range(10000000):
    next(g1)
    next(g2)
​
print(isum)
end_time = time.time()
cost_time = end_time - start_time
print('花费了%.f秒' % cost_time)

 

 

进程线程协程

进程,分配资源的单位

进程中必定有一个主线程

 

线程,是执行任务的单位

 

协程,它在线程之内。我们利用了生成器它可以保存状态,可以暂停可以恢复。人为行设计出来的一种多任务的模式。

 

进程包含线程,线程包含协程。

 

 

协程的应用

生产者消费者模型,两个版本

关注切换的主动权在谁

 

你拍一我拍一双人共同玩游戏

 

生成器send方法

 

 

greenlet提高切换效率

yield会返回

用greenlet对象直接通过switch方法切换到另一函数中

 

greenlet的用法

导入模式

 

 

 

 

 

 

import greenlet

 

 

greenlet 一种绿色小鸟

 

创建对象

 

 

 

 

 

 

对象 = greenlet.greenlet(test1)

 

 

 

运行

 

 

 

 

 

 

对象.switch()

 

 

switch 英 [swɪtʃ] 美 [swɪtʃ] n. (电路的)开关,闸,转换器;(尤指突然彻底的)改变,转变;(铁路的)转辙器,道岔 v. (使)改变,转变,突变;交换;掉换;转换;对调;调班;临时掉换工作时间

 

 

明确,为什么要多任务

原因

不想等

真正开发的过程中

不能够明确什么时候要等,什么时候要等多久

 

 

a 烧水 15 1 1

b 煮汤 30 1 1

c 挂机 1h 1 1

 

 

 

gevent

 

event 英 [ɪˈvent] 美 [ɪˈvent] n. 发生的事情;(尤指)重要事情,大事;公开活动;社交场合;(体育运动的)比赛项目

 

 

导入模块

 

 

 

 

 

 

import gevent

 

创建对象

 

 

 

 

 

 

对象 = gevent.spawn(函数名)

 

spawn 英 [spɔːn] 美 [spɔːn] v. 产卵;引发;引起;导致;造成 n. (鱼、蛙等的)卵

 

运行

 

 

 

 

 

 

对象.join() 

 

当gevent对象遇到join方法的阻塞时,就会自动去gevent对象中去看

有没有可以执行的代码

查询范围是所有的对象,不仅仅是只查自己对应的函数的代码

 

导入猴子

 

 

 

 

 

 

from gevent import monkey
monkey.patch_all()

 

gevent对象不能够识别常见的一些耗时操作

因此需要猴子来打一个补丁

 

 

注意,只能使用from...import来导入

不可以通过gevent.monkey.patch_all()调这个函数

 

 

monkey 英 [ˈmʌŋki] 美 [ˈmʌŋki] n. 猴子;顽皮的孩子;调皮鬼;捣蛋鬼;500英镑 第三人称单数: monkeys 复数: monkeys 现在分词: monkeying 过去式: monkeyed 过去分词: monkeyed

 

patch 英 [pætʃ] 美 [pætʃ] n. 色斑;斑点;(与周围不同的)小块,小片;补丁;补块;眼罩 v. 打补丁;缝补;修补 第三人称单数: patches 复数: patches 现在分词: patching 过去式: patched 过去分词: patched CET4考研TOEFLT

 

all 英 [ɔːl] 美 [ɔːl] det. 所有;全部;全体;一切;(与单数名词连用,表示某事在某段时间内持续发生)全部的,整个的 pron. 所有;全部;全体;一切;唯一的事物;所有的事物 adv. 完全;很;十分;非常;太;过分

 

 

select

select模块的使用: select会监听socket或者文件描述符的I/O状态变化,并返回变化的socket或者文件描述符对象

select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)

 

这是Python select方法的原型,接收4个参数

  1. rlist:list类型,监听其中的socket或者文件描述符是否变为可读状态,返回那些可读的socket或者文件描述符组成的list

  2. wlist:list类型,监听其中的socket或者文件描述符是否变为可写状态,返回那些可写的socket或者文件描述符组成的list

  3. xlist:list类型,监听其中的socket或者文件描述符是否出错,返回那些出错的socket或者文件描述符组成的list

  4. timeout:设置select的超时时间,设置为None代表永远不会超时,即阻塞。

 

注意:

Python的select方法在Windows和Linux环境下的表现是不一样的,Windows下它只支持socket对象,不支持文件描述符(file descriptions),而Linux两者都支持。

我们可以通过打印来查看select模块提供的作用,返回的rlist,wlist只会返回有改变的监听对象,如果没有改变的函数,那么整个程序会阻塞住

如果我们想要加入新的连接,那么我们只需要把连接对象放进rlist即可,当有数据过来的时候,那么连接就会发生改变(文件描述符),select函数就会帮我们监听到

如果我们想发送数据,那么我们可以把conn加入到wlist,因为发送数据需要我们去输出流数据,然后等待select把wlist里面的消息取出来,我们就可以发送数据了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值