Python并发编程系列之协程

  4.1 单个协程

  4.2多协程并发

  4.3 获取返回值

  4.4 绑定回调函数

  4.5 协程的嵌套使用

5 总结

 

1 引言

  协程是近几年并发编程的一个热门话题,与Python多进程、多线程相比,协程在很多方面优势明显。本文从协程的定义和意义出发,结合asyncio模块详细讲述了协程的使用。

2 协程的意义 2.1 什么是协程

  协程,又称微线程,英文名为Coroutine。对于多线程,在执行一个个不同任务时,遇到阻塞(例如IO操作)时,操作系统会自动将CPU资源切换给另一个线程。

2.2 协程的作用

         与线程不同的是,协程需要用户自己进行手动切换——当某线程在执行任务中的函数A(协程A))时,可任意终端,手动切换到任务中的另一个函数B(协程B),然后在适当的时候在回到函数A(协程A)中继续执行,这样虽然繁琐,但也提供了更大的操作自由度,同时协程A和协程B都属于同一线程,切换效率相比于线程或进程间的切换有极大地优势。另外,协程不需要多线程的锁机制,因为都属于同一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

         我们以爬虫为例子,说明协程的应用。启动一个爬虫程序一定属于一个进程,这是毋庸置疑的,但进程本身并不会执行任何操作,所有操作都是通过线程来完成,所以一个进程有一个主线程。一般爬虫的步骤包括发送request请求、写入文件等操作,而这些都是IO操作,当线程执行到这些操作时,要么等待这一操作完成要么切换到其他线程。如果使用了协程呢?如果协程遇到了此类IO操作,可以立即切换到其他操作,例如直接发送下一个request请求,甚至发送第二个、第三个请求……直至原来的协程中IO请求完成,那么回到原来的协程继续下一步操作。这就是协程的工作原理,充分利用线程的工作效率,也没有多线程切换的开销,所以在处理IO操作时协程非常高效。

       简单总结一下协程的优缺点:

       优点:

  1)无需线程上下文切换的开销(还是单线程);

  2)无需原子操作的锁定和同步的开销;

  3)方便切换控制流,简化编程模型;

  4)高并发+高扩展+低成本:一个cpu支持上万的协程都没有问题,适合用于高并发处理。

  缺点:

  1)无法利用多核的资源,协程本身是个单线程,它不能同时将单个cpu的多核用上,协程需要和进程配合才能运用到多cpu上(协程是跑在线程上的);

  2)进行阻塞操作时会阻塞掉整个程序:如io;

       现在,各位读者应该已经对协程的概念又说了解了,也感受到了协程的魅力了吧!那么该怎么使用协程了……

2.3 相关概念

  在Python中, asyncio、tornado和gevent等模块都实现了协程的功能。本篇中主要介绍asyncio。

  在介绍通过asyncio的使用协程之前,首先有必要先介绍一下asyncio中涉及的几个概念,要想掌握asyncio这这几个贯穿始终的概念必须好好理解:

  1)event_loop事件循环:程序开启一个无限的循环,程序员会把一些函数(协程)注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。

  2)coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。

  3)future 对象: 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别

  4)task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。Task 对象是 Future 的子类,它将coroutine和Future联系在一起,将 coroutine 封装成一个 Future 对象。

  5)async/await 关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口,await就类似于说下面过程阻塞,暂时执行别的协程。await关键字添加了一个新的协程到循环里,而不需要明确地添加协程到这个事件循环里。

3 定义协程

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgwxxj.html