1 协程

1.1协程的概念

  协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。(其实并没有说明白~)

  我觉得单说协程,比较抽象,如果对线程有一定了解的话,应该就比较好理解了。

  那么这么来理解协程比较容易:

  线程是系统级别的,它们是由操作系统调度;协程是程序级别的,由程序员根据需要自己调度。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序,这就是协程。也就是说同一线程下的一段代码<1>执行着执行着就可以中断,然后跳去执行另一段代码,当再次回来执行代码块<1>的时候,接着从之前中断的地方开始执行。

  比较专业的理解是:

  协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

1.2 协程的优缺点

协程的优点:

  (1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)

  (2)无需原子操作锁定及同步的开销

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

  (4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

协程的缺点:

  (1)无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

  (2)进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

2 Python中如何实现协程

2.1 yield实现协程  

  前文所述“子程序(函数)在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序”,那么很容易想到Python的yield,显然yield是可以实现这种切换的。

使用yield实现协程操作例子:

 #! /usr/bin/env python
# -*- coding:utf-8 -*-
# Author: "Zing-p"
# Date: 2017/5/12 def consumer(name):
print("要开始啃骨头了...")
while True:
print("\033[31;1m[consumer] %s\033[0m " % name)
bone = yield
print("[%s] 正在啃骨头 %s" % (name, bone)) def producer(obj1, obj2):
obj1.send(None) # 启动obj1这个生成器,第一次必须用None <==> obj1.__next__()
obj2.send(None) # 启动obj2这个生成器,第一次必须用None <==> obj2.__next__()
n = 0
while n < 5:
n += 1
print("\033[32;1m[producer]\033[0m 正在生产骨头 %s" % n)
obj1.send(n)
obj2.send(n) if __name__ == '__main__':
con1 = consumer("消费者A")
con2 = consumer("消费者B")
producer(con1, con2)

运行的结果:

2.2 greenlet实现协程

  Python的 greenlet就相当于手动切换,去执行别的子程序,在“别的子程序”中又主动切换回来。。。

greenlet协程例子:
 #! /usr/bin/env python
# -*- coding:utf-8 -*- from greenlet import greenlet
# greenlet 其实就是手动切换;gevent是对greenlet的封装,可以实现自动切换 def test1():
print("")
gr2.switch() # 切换去执行test2
print("")
gr2.switch() # 切换回test2之前执行到的位置,接着执行 def test2():
print("")
gr1.switch() # 切换回test1之前执行到的位置,接着执行
print("") gr1 = greenlet(test1) # 启动一个协程 注意test1不要加()
gr2 = greenlet(test2) #
gr1.switch()

2.3  gevent 实现协程

  Gevent 是一个第三方库,可以轻松通过gevent实现协程程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

  gevent会主动识别程序内部的IO操作,当子程序遇到IO后,切换到别的子程序。如果所有的子程序都进入IO,则阻塞。

协程之gevent例子:

 #! /usr/bin/env python3
# -*- coding:utf-8 -*- import gevent def func1():
print("func1 running")
gevent.sleep(2) # 内部函数实现io操作
print("switch func1") def func2():
print("func2 running")
gevent.sleep(1)
print("switch func2") def func3():
print("func3 running")
gevent.sleep(0)
print("func3 done..") gevent.joinall([gevent.spawn(func1),
gevent.spawn(func2),
gevent.spawn(func3),
])

同步与异步性能区别:

 import gevent

 def task(pid):
"""
Some non-deterministic task
"""
gevent.sleep(0.5)
print('Task %s done' % pid) def synchronous():
for i in range(1,10):
task(i) def asynchronous():
threads = [gevent.spawn(task, i) for i in range(10)]
gevent.joinall(threads) print('Synchronous:')
synchronous() print('Asynchronous:')
asynchronous()

  上面程序的重要部分是将task函数封装到greenlet内部线程的gevent.spawn。 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,后者阻塞当前流程,并执行所有给定的greenlet。执行流程只会在 所有greenlet执行完后才会继续向下走。

遇到Io阻塞时会切换任务之【爬虫版】

 #! /usr/bin/env python3
# -*- coding:utf-8 -*- from urllib import request
import gevent,time
from gevent import monkey monkey.patch_all() # 把当前程序中的所有io操作都做上标记 def spider(url):
print("GET:%s" % url)
resp = request.urlopen(url)
data = resp.read()
print("%s bytes received from %s.." % (len(data), url)) urls = [
"https://www.python.org/",
"https://www.yahoo.com/",
"https://github.com/"
] start_time = time.time()
for url in urls:
spider(url)
print("同步耗时:",time.time() - start_time) async_time_start = time.time()
gevent.joinall([
gevent.spawn(spider,"https://www.python.org/"),
gevent.spawn(spider,"https://www.yahoo.com/"),
gevent.spawn(spider,"https://github.com/"),
])
print("异步耗时:",time.time() - async_time_start) # 最好爬国外网站吧

通过gevent实现【单线程】下的多socket并发

server端:

 import sys
import socket
import time
import gevent from gevent import socket,monkey
monkey.patch_all() def server(port):
s = socket.socket()
s.bind(('0.0.0.0', port))
s.listen(500)
while True:
cli, addr = s.accept()
gevent.spawn(handle_request, cli) def handle_request(conn):
try:
while True:
data = conn.recv(1024)
print("recv:", data)
conn.send(data)
if not data:
conn.shutdown(socket.SHUT_WR) except Exception as ex:
print(ex)
finally:
conn.close()
if __name__ == '__main__':
server(9999)

client端:

 import socket

 HOST = 'localhost'    # The remote host
PORT = 9999 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
msg = bytes(input(">>:"),encoding="utf8")
s.sendall(msg)
data = s.recv(1024)
#print(data) print('Received', repr(data))
s.close()

  觉得将就点个赞~~

协程及Python中的协程的更多相关文章

  1. python中的协程及实现

    1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...

  2. python中的协程:greenlet和gevent

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

  3. 三、进程和线程、协程在python中的使用

    三.进程和线程.协程在python中的使用 1.多进程一般使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,当然生产者消费者这种也可以使用.多进程的优势就是一个子 ...

  4. Python中异步协程的使用方法介绍

    1. 前言 在执行一些 IO 密集型任务的时候,程序常常会因为等待 IO 而阻塞.比如在网络爬虫中,如果我们使用 requests 库来进行请求的话,如果网站响应速度过慢,程序一直在等待网站响应,最后 ...

  5. Python中Paramiko协程方式详解

    什么是协程 协程我们可以看做是一种用户空间的线程. 操作系统对齐存在一无所知,需要用户自己去调度. 比如说进程,线程操作系统都是知道它们存在的.协程的话是用户空间的线程,操作系统是不知道的. 为什么要 ...

  6. python中的协程

    目录 协程是啥 协程和线程差异 简单实现协程 greenlet 安装方式 gevent 安装 1. gevent的使用 2. gevent切换执行 3. 给程序打补丁 进程.线程.协程对比 请仔细理解 ...

  7. python中的协程并发

    python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程.无论多线程和多进程,IO的调度更多取决于系统,而协程的方式,调度来自用户,用户可以在函数中yield一个状态 ...

  8. python中多进程+协程的使用以及为什么要用它

    前面讲了为什么python里推荐用多进程而不是多线程,但是多进程也有其自己的限制:相比线程更加笨重.切换耗时更长,并且在python的多进程下,进程数量不推荐超过CPU核心数(一个进程只有一个GIL, ...

  9. Python | 详解Python中的协程,为什么说它的底层是生成器?

    今天是Python专题的第26篇文章,我们来聊聊Python当中的协程. 我们曾经在golang关于goroutine的文章当中简单介绍过协程的概念,我们再来简单review一下.协程又称为是微线程, ...

随机推荐

  1. Sonar+maven+jenkins集成,Java代码走查

    Sonar服务在Sonar安装与使用篇已经介绍过,此文章不再说了 Jenkins的安装与配置方法参考http://www.cnblogs.com/chenchen-tester/p/6408815.h ...

  2. android 自定义 Application

    android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且application对象的生命周 ...

  3. CSS学习总结4:派生选择器学习总结

    派生选择器:通过依据元素在其位置的上下文关系来定义样式,你可以使标记更加简洁.派生选择器中一共分为三种:后代选择器.子元素选择器.相邻兄弟选择器. 1.初识派生选择器 实例:你希望列表中的 stron ...

  4. 选择困难症的福音——团队Scrum冲刺阶段-Day 2

    选择困难症的福音--团队Scrum冲刺阶段-Day 2 今日进展 编写提问部分 如何将不同的问题选项连接到不同的下一个问题 如何保证问题的链接不会弄丢 登陆注册界面 完成密码可见与不可见的更改 ui界 ...

  5. LAMP简介与部署

    lamp简介 lamp,是由Linux+Apache+Mysql/MariaDB+Php/Perl/Python的一组动态网站或者服务器的开源软件,除Linux外其它各部件本身都是各自独立的程序,但是 ...

  6. ABP框架系列之五:(Unit Of Work-工作单元)

    Introduction Connection and transaction management is one of the most important concepts in an appli ...

  7. kei下无法跳转到函数的定义处

    1 勾选“option for target”----“output”----"Browse information" 2 重新编译整个工程, 执行上面两个步骤就可以跳转了.

  8. getResource()和getResourceAsStream以及路径问题

    用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file ...

  9. AJAX笔记整理

    AJAX: Asynchronous JavaScript and XML,异步的Javascirpt和Xml. Asynchronous:异步 与之对应的是 synchronous:同步,我们要知道 ...

  10. 17.异常(三)之 e.printStackTrace()介绍

    一.关于printStackTrace()方法 public void printStackTrace()方法将此throwable对象的堆栈追踪输出至标准错误输出流,作为System.err的值.输 ...