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. c#中list使用示例

    protected void Page_Load(object sender, EventArgs e) { List<string> studentNames = new List< ...

  2. 关于form表单上传图片的一些记录

    jsp页面 <form class="form-horizontal" role="form" action="brandAction_addB ...

  3. MXBridge - 插件式JS与OC交互框架

    概述 MXBridge,提供一个插件式的JavaScript与Objective-C交互的框架,通过JavaScriptCore实现,插件式扩展Obejctive-C接口以供JavaScript调用. ...

  4. SGU 223 Little Kings(状压DP)

    Description 用字符矩阵来表示一个8x8的棋盘,'.'表示是空格,'P'表示人质,'K'表示骑士.每一步,骑士可以移动到他周围的8个方格中的任意一格.如果你移动到的格子中有人质(即'P'), ...

  5. LeetCode 54. Spiral Matrix(螺旋矩阵)

    Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral or ...

  6. R学习笔记 第五篇:数据变换和清理

    在使用R的分组操作之前,首先要了解R语言包,包实质上是实现特定功能的,预先写好的代码库(library),R拥有大量的软件包,许多包都是由某一领域的专家编写的,但并不是所有的包都有很高的质量的,在使用 ...

  7. Java 链表常见考题总结

    首先定义自定义结点类,存储节点信息: public class Node { Node next=null; int data; public Node(int data){ this.data=da ...

  8. vim基本命令(转载自网络)

    来源于<Unix初级教程(第四版)>. 命令模式切换到文本输入模式: 键 功能 i 在光标左侧输入文本 I 在当前行的行首输入文本 a 在光标右侧输入文本 A 在当前行的行尾输入文本 o ...

  9. 扩展 lua require 的行为

    扩展 lua require 的行为 来源 https://blog.codingnow.com/2015/10/lua_require_env.html 今天同事提了个需求,他希望可以给部分 lua ...

  10. java三级考试理论题

    Java试卷 一.单项选择题: 1. 试题: 在WINDOWS环境下,ODBC在__A____中设置 A. 控制面板 B. 注册表 C. 浏览器 D. 写字板 2. 阅读以下描述: C <jsp ...