1. 什么是协程?

  协程(coroutine),又称微线程。协程不是线程也不是进程,它的上下文关系切换不是由CPU控制,一个协程由当前任务切换到其他任务由当前任务来控制。一个线程可以包含多个协程,对于CPU而言,不存在协程这个概念,它是一种轻量级用户态线程(即只针对用户而言)。协程拥有自己的寄存器上下文和栈,协程调度切换到其他协程时,将寄存器上下文和栈保存,在切回到当前协程的时候,恢复先前保存的寄存器上下文和栈。

2. 在编程中为什么要使用协程?

  使用协程的好处:(1)CPU无需负担上下文的开销;(2)不需加锁(多个线程操作数据时得加锁);(3)由程序员切换控制流,方便编程;(4)高并发、高扩展、低成本(一个CPU支持上万的协程都不是问题)。

   当然,任何事物有优点必有缺点。协程得缺点:(1)协程自己无法利用CPU多核资源(除非与多进程或者多线程配合);(2)遇到阻塞操作会使整个程序阻塞。

例一(使用yield实现在任务间的切换):

 import time

 def func1(name):
print("----func1 start...----")
for i in range(6):
temp = yield #每次遇到yield,func1在此处阻塞,直到temp接收到func2中con.send()传来的值
print("%s in the func1" % (str(temp)))
time.sleep(1) def func2():
print("----func2 start...----")
con.__next__() #此处开始真正的func1的调用
for i in range(5):
con.send(i+1)
print("%s in the func2" % i) if __name__ == '__main__':
con = func1(1) #在有yield的函数中此处不是真正的函数调用,打印con便可知道
# print(con)
p = func2()

使用yield进行任务切换

  注:例一严格来说不能算是协程,只是实现了两个任务之间的切换。

3. 既然例一不能算多协程,难么在python中应该如何使用协程?

  greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator(例一中的con=func1(1)就是做这个操作)。

例二:

 import greenlet

 def func1():
for i in range(1,6):
print(i)
g2.switch() #切换到g2 def func2():
words = ['a', 'b', 'c', 'd', 'e']
for w in words:
print(w)
g1.switch() #切换到g1 g1 = greenlet.greenlet(func1)
g2 = greenlet.greenlet(func2)
g1.switch() #切换到g1

使用greenlent模块实现任务切换

  注:使用greenlent可以很简单的进行多任务之间的切换,但是程序运行最耗时的便是I/O操作,要使用协程实现高并发,应当是一旦遇到I/O操作就切换到其他任务,等I/O操作完成后在切回到当前任务(这个过程应当是自动的)。

4. 那么在python中,如何让任务遇到I/O操作就切换?

  我们使用第三方库gevent来实现。

  gevent的官方定义:gevent is a coroutine -based Python networking library that uses greenlet to provide a high-level synchronous API on top of the libev event loop.

例三(gevent的简单使用):

 import gevent
import time def func1():
print("func1: start.....")
# Put the current greenlet to sleep for at least *seconds*.(模拟I/O操作,任务在此处自动切换)
gevent.sleep(3)
print("func1: end") def func2():
print("func2: start.....")
gevent.sleep(0.5)
print("func2: end") start_time = time.time()
# joinall(greenlets, timeout=None, raise_error=False, count=None)
# Wait for the ``greenlets`` to finish.
# :return: A sequence of the greenlets that finished before the timeout (if any)expired.
gevent.joinall([gevent.spawn(func1),
gevent.spawn(func2)])
# spawn(cls, *args, **kwargs)
# Create a new :class:`Greenlet` object and schedule it to run ``function(*args, **kwargs)``.
# This can be used as ``gevent.spawn`` or ``Greenlet.spawn``. print("cost:", time.time()-start_time)
# 通过计算程序运行的时间可以发现程序确实是以单线程达模拟出了多任务并行的操作。

gevent的简单使用

例四(gevent和urllib配合同时下载多个网页):

 import urllib.request
import gevent,time
import gevent.monkey def func(url="", filename=""):
print("Download:%s" % url)
result = urllib.request.urlopen(url) #请求打开一个网页
data = result.read() #读取内容
with open(filename, 'wb') as fp: #写入文档
fp.write(data)
print("Finish:%s" % url) if __name__ == "__main__":
# Do all of the default monkey patching (calls every other applicablefunction in this module).
# 相当与做一个标记,做完此操作gevent就可以检测到此程序中所有的I/O操作
gevent.monkey.patch_all() async_time = time.time()
gevent.joinall([
gevent.spawn(func, "http://www.cnblogs.com/God-Li/p/7774497.html", "7774497.html"),
gevent.spawn(func, "http://www.gevent.org/", "gevent.html"),
gevent.spawn(func, "https://www.python.org/", "python.html"),
])
print("async download cost:", time.time()-async_time) start_time = time.time()
func("http://www.cnblogs.com/God-Li/p/7774497.html", "7774497.html")
func("http://www.gevent.org/", "gevent.html")
func("https://www.python.org/", "python.html")
print("download cost:", time.time()-start_time)

gevent和urllib配合同时下载多个网页

  注:对上例代码稍加改造,加上对html源码的解析功能,就可以实现一个简单的多并发爬虫。

python --- 网络编程Socket中例二的socket_server2使用gevent改造就可以使其成为一个大并发的socket server。

例五(使用gevent实现并发的socket server):

 #服务端
import socket
import gevent
import gevent.monkey gevent.monkey.patch_all() def request_handler(conn): '''
Wait for an incoming connection. Return a new socket
representing the connection, and the address of the client.
'''
while True:
# print("ok")
data = conn.recv(1024) #接收信息,写明要接收信息的最大容量,单位为字节
print("server recv:", data)
conn.send(data.upper()) #对收到的信息处理,返回到客户端 if __name__ == "__main__":
address = ("localhost", 6666) # 写明服务端要监听的地址,和端口号
server = socket.socket() # 生成一个socket对象
server.bind(address) # 用socket对象绑定要监听的地址和端口
server.listen() # 开始监听 while True:
conn, addr = server.accept() # 等带新连接接入服务端,返回一个新的socket对象和地址,地址格式同前面格式
gevent.spawn(request_handler, conn) server.close() # 关闭服务端

socket_server2的并发实现

  注:可使用python --- 网络编程Socket中例二的socket_client2进行测试。

python --- 协程编程(第三方库gevent的使用)的更多相关文章

  1. python协程详解,gevent asyncio

    python协程详解,gevent asyncio 新建模板小书匠 #协程的概念 #模块操作协程 # gevent 扩展模块 # asyncio 内置模块 # 基础的语法 1.生成器实现切换 [1] ...

  2. python基于协程的网络库gevent、eventlet

    python网络库也有了基于协程的实现,比较著名的是 gevent.eventlet 它两之间的关系可以参照 Comparing gevent to eventlet, 本文主要简单介绍一下event ...

  3. day-5 python协程与I/O编程深入浅出

    基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1.  什么是协程(以下内容来自维基百 ...

  4. python中的协程:greenlet和gevent

    python中的协程:greenlet和gevent 协程是一中多任务实现方式,它不需要多个进程或线程就可以实现多任务. 1.通过yield实现协程: 代码: import time def A(): ...

  5. Python协程(真才实学,想学的进来)

    真正有知识的人的成长过程,就像麦穗的成长过程:麦穗空的时候,麦子长得很快,麦穗骄傲地高高昂起,但是,麦穗成熟饱满时,它们开始谦虚,垂下麦芒. --蒙田<蒙田随笔全集> *** 上篇论述了关 ...

  6. Python协程与Go协程的区别二

    写在前面 世界是复杂的,每一种思想都是为了解决某些现实问题而简化成的模型,想解决就得先面对,面对就需要选择角度,角度决定了模型的质量, 喜欢此UP主汤质看本质的哲学科普,其中简洁又不失细节的介绍了人类 ...

  7. python 协程与go协程的区别

    进程.线程和协程 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定义: 操作系统能够进行运算调度的最小单位.它被包含在进 ...

  8. Python协程与JavaScript协程的对比

    前言 以前没怎么接触前端对JavaScript 的异步操作不了解,现在有了点了解一查,发现 python 和 JavaScript 的协程发展史简直就是一毛一样! 这里大致做下横向对比和总结,便于对这 ...

  9. [转载] Python协程从零开始到放弃

    Python协程从零开始到放弃 Web安全 作者:美丽联合安全MLSRC   2017-10-09  3,973   Author: lightless@Meili-inc Date: 2017100 ...

随机推荐

  1. Oracle 之——子查询 DDL DML 集合 及其他数据对象

    Oracle 学习笔记(二) 知识概要: 1.子查询 2.集合操作 3.DML语句操作 4.其他数据库对象 1.子查询 查询工资比SCOTT高的员工信息 1  select * 2  from emp ...

  2. PDO浅谈之php连接mysql

    一.首先我们先说一下什么是pdo?  百科上说 PDO扩展为PHP访问数据库定义了一个轻量级的.一致性的接口,它提供了一个数据访问抽象层,这样,无论使用什么数据库,都可以通过一致的函数执行查询和获取数 ...

  3. ssi服务器端指令详解(shtml)

    你是否曾经或正在为如何能够在最短的时间内完成对一个包含上千个页面的网站的修改而苦恼?那么可以看一下本文的介绍,或许能够对你有所帮助. 什么是SSI? SSI是英文Server Side Include ...

  4. SQLite中的时间日期函数

    SQLite包含了如下时间/日期函数: datetime().......................产生日期和时间 date()...........................产生日期 t ...

  5. PHP连接mysql数据库进行增删改查--删除

    删除: 1.首页 在foreach里面加入   <td><a href='dele.php?id={$i[0]}'>删除</a></td> 在上面< ...

  6. Java 集合框架之set用法

    Java 集合框架之set 一个简单的例子 创建一个Customer类,类中的属性有姓名(name).年龄(age).性别(gender),每个属性分别有get/set 方法.然后创建两个Custom ...

  7. 赋值运算符函数__from <剑指Offer>

    前段时间忙于项目,难得偷得几日闲,为即将到来的就业季做准备.在面试时,应聘者要注意多和考官交流,只有具备良好的沟通能力,才能充分了解面试官的需求,从而有针对性地选择算法解决问题. 题目来源于<剑 ...

  8. 求原码、补码,反码(C语言源代码)

    #include <stdio.h> #define N 8 //这里你要求是8位 int main(int argc, const char * argv[]) { int binary ...

  9. 有序线性表(存储结构数组)--Java实现

    /*有序数组:主要是为了提高查找的效率 *查找:无序数组--顺序查找,有序数组--折半查找 *其中插入比无序数组慢 * */ public class MyOrderedArray { private ...

  10. Java基础-流程控制(04)

    在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的.也就是说程序的流程对运行结果有直接的影响.所以,我们必须清楚每条语句的执行流程.而且,很多时候我们要通过控制语句的执行顺序来实现我 ...