python协程

python协程基础知识

阻塞

-阻塞状态是指程序未得到所需计算资源时被挂起的状态。程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的
-常见的阻塞形式
    -网络I/O阻塞
    -磁盘I/O阻塞
    -用户输入阻塞等
-阻塞是无处不在的,包括在CPU切换上下文时,所有进程都无法真正干事情,它们也会被阻塞。在多核CPU的情况下,正在执行上下文切换操作的核不可被利用

非阻塞

-程序在等待某操作的过程中,自身不被阻塞,可以继续干别的事情,则称该程序在该操作上是非阻塞的
-非阻塞并不是在任何程序级别、任何情况下都存在的。仅当程序封装的几倍可以囊括独立的子程序单元时,程序才可能存在非阻塞状态
-非阻塞因阻塞存在而存在,正因为阻塞导致程序运行的耗时增加与效率低下,我们菜肴把他变成非阻塞的

同步

-不同程序单元为了共同完成某个任务,在执行过程中需要靠某种通信方式保持协调一致,此时这些程序单元时同步执行的
-例如
    -在购物系统中更新商品库存时,需要用”行锁“作为通信信号,强制让不同的封信请求排队并按顺序执行,这里的更新库存操作就是同步的
-简言之,同步意味着有序

异步

-为了完成某个任务,有时不同程序单元之间无须通信协调也能完成任务,此时不相关的程序单元之间可以是异步的
-例如
    -爬取下载网页。调度程序调用下载程序后,即可调度其他任务,无须与该下载任务保持通信以协调行为。不同网页的下载、保存等操作都是无关的,
    也无须相互通知协调。这些异步操作的完成时刻并不确定
-简言之,异步意味着无序

多进程

-多进程就是利用CPU的多核优势,在同一时间并行执行多个任务,可以大大提高执行效率

协程

-协程,英文叫做 coroutine,又称微线程、纤程,是一种运行在用户太的轻量级线程
-协程拥有自己的寄存器上下文和栈。协程在调度切换时,将寄存器上下文和栈保存到其他地方,等切回来的时候,再恢复先前保存的寄存器上下文和栈
因此,协程能保留上一次调用时的状态,即所有局部状态的一个特定组合,每次过程重入,就相当于进入上一次调用的状态
-协程本质上是个单进程,相对于多进程来说,他没有线程上下文切换的开销,没有原子操作锁定即同步的开销,编程模型也非常简单
-我们可以使用协程来实现异步操作,例如
    -在网络爬虫的场景下,我们发出一个请求之后,需要等待一定时间才能得到响应,但其实在这个等待过程中,
    程序可以干许多其他事情,等得到响应之后再切换回来继续处理,这样可以充分利用CPU和其他资源,这就是协程的优势。

协程的用法

-常用库asyncio

python import asyncio

-envet_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足发生条件的时候,就调用对应的处理方法
-coroutine:中文翻译叫协程,在python中常指代协程对象类型,我们可以将协程对象注册到事件循环中,他会被时事件循环调用。
我们可以使用async关键字来定义一个方法,这个方法在调用时不会立即被执行,而是会返回一个协程对象。
-task:任务,这是对协程对象的进一步封装,包含协程对象的各个状态。
-future:代表将来执行或者没有执行的任务的结果,实际上和task没有本质区别
-另外,我们还需要了解async、await关键字,他们是从Python3.5才开始出现的,专门用于定义协程。
    -async,用来定义一个协程
    -await,用来挂起阻塞方法的执行

定义协程

1.

- async定义的方法会变成一个无法直接执行的协程对象,必须将此对象注册到事件循环中才可以执行
import asyncio  # 导入asyncio包,这样才可以使用async和await关键字


# 使用async定义一个execute方法,该方法接受一个数字参数x,执行之后会打印这个数字
async def execute(x):
    print('Number:', x)

# 调用execute,该方法不会执行,会返回一个coroutine协程对象
coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')

# 使用asyncio包中的get_event_loop()方法创建一个事件循环loop
loop = asyncio.get_event_loop()
# 调用loop对象的run_until_complete方法将协程对象注册到事件循环中,execute会执行,打印了数字1
loop.run_until_complete(coroutine)
print('After calling loop')    
-前面我们还提到了task,他是对协程对象的进一步封装,比协程对象多了运行状态,
例如running、finished等,我们可以利用这些状态获取协程对象的执行情况

2.

-在上面的例子中,当我们把协程对象coroutine传递给run_until_complete方法的时候
实际上它进行了一个操作,就是将coroutine封装成task对象。对此我们也可以显式的进行声明
import asyncio  



async def execute(x):
    print('Number:', x)


coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')

loop = asyncio.get_event_loop()
# 将coroutine封装成task对象
task = loop.create_task(coroutine)
print("Task:", task)
loop.run_until_complete(task)
print("Task:",task)
print('After calling loop')
-这里我们定义了一个loop对象之后,紧接着调用了它的create_task方法,将协程对象转化为task对象,随后打印输出一下
发现它处于pending状态。然后将task对象添加到事件循环中执行,并再次打印出task对象,发现它的状态变成了finished
同时还可以看到其result变成了1,也就是我们定义的execute方法的返回结果

3.

-定义task对象还有另外一种方式,就是直接调用asyncio包的ensure_future方法,返回结果也是task对象
这样的话我们就可以不借助loop对象。即使还没有声明loop,也可以提前定义好task对象
import asyncio



async def execute(x):
    print('Number:', x)


coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')

# 直接调用asyncio包中的ensure_future方法
task = asyncio.ensure_future(coroutine)
print("Task:", task)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)
print("Task:",task)
print('After calling loop')

绑定回调

-我们也可以为某个task对象绑定一个回调方法,例子
import asyncio
import requests


async def request():
    url = "https://www.baidu.com"
    status = requests.get(url)
    return status


def callback(task):
    print("Status:", task.result())


coroutine = request()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
print("Task:", task)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)
print("Task:", task)
-这里我们定义了request方法,在这个方法里请求了百度,并获取了其状态码,但是没有编写任何print语句
随后我们定义了callback方法,这个方法接收一个参数,参数是task对象,再这个方法中调用print方法打印
出了task对象的结果。这样就定义好了一个协程对象和一个回调方法。我们现在希望达到的效果是,当协程对象执行完毕之后
就去执行声明的callback方法。
那两者怎样关联起来呢?很简单,只要调用add_done_callback方法就行。我们将callback方法传递给封装好的task对象,
这样当task执行完毕之后,就可以调用callback方法了。同时task对象还会作为参数传递给callback方法,
调用task对象的result方法就可以获取返回结果了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值