python中协程
在引出协成概念之前先说说python的进程和线程。
进程:
进程是正在执行程序实例。执行程序的过程中,内核会讲程序代码载入虚拟内存,为程序变量分配空间,建立 bookkeeping 数据结构,来记录与进程有关的信息,
比如进程 ID,用户 ID 等。在创建进程的时候,内核会为进程分配一定的资源,并在进程存活的时候不断进行调整,比如内存,进程创建的时候会占有一部分内存。
进程结束的时候资源会释放出来,来让其他资源使用。我们可以把进程理解为一种容器,容器内的资源可多可少,但是在容器内的程序只能使用容器内的东西。因此启动
进程的时候会比较慢,尤其是windows,尤其是多进程的时候(最好是在密集性运算的时候启动多进程)
线程:
一个进程中可以执行多个线程。多个线程共享进程内的资源。所以可以将线程可以看成是共享同一虚拟内存以及其他属性的进程。
线程相对于进程的优势在于同一进程下的不同线程之间的数据共享更加容易。
在说到线程的时候说说GIL(全局解释性锁 GLOBAL INTERPRETER LOCK),GIL 的存在是为了实现 Python 中对于共享资源访问的互斥。而且是非常霸道的解释器级别的互斥。在 GIL 的机制下,一个线程访问解释器之后,其他的线程就需要等待这个线程释放之后才可以访问。这种处理方法在单处理器下面并没有什么问题,单处理器的本质是串行执行的。但是再多处理器下面,这种方法会导致无法利用多核的优势。Python 的线程调度跟操作系统的进程调度类似,都属于抢占式的调度。一个进程执行了一定时间之后,发出一个信号,操作系统响应这个时钟中断(信号),开始进程调度。而在 Python 中,则通过软件模拟这种中断,来实现线程调度。比如:对全局的num做加到100的操作,可能在你加到11的时候,还没加完,则CPU就交给另一个线程处理,所以最后的结果可能比100会小或者比100会大。
简单的说说进程和线程的几点关系
1、启动一个进程至少会有一个线程
2、修改主线程的数据会影响到子线程的数据,因为他们之间内存是共享的,修改主进程不会影响到子进程的数据,两个子进程之间是相互独立的,如果要实现子进程间的通信,可以利用中间件,比如multiprocessing的Queue。
如:
#!/usr/bin/env python
# -*- coding:utf-8 -*- #进程之间的通信
from multiprocessing import Process,Queue def f(qq):
#在子进程设置值,本质上是子进程pickle数据序列化到公共的地方
qq.put(['hello',None,123]) if __name__ == '__main__':
q = Queue()
t = Process(target=f,args=(q,))
t.start()
#从父进程中取出来,本质上是父进程pickle从公共的地方把数据反序列化出来
print q.get()
t.join()
3、新的线程很容易被创建,但是新的进程需要对其父进程进行一次克隆
4、一个线程可以操作和控制同一个进程里的其他线程,但进程只能操作其子进程。
明白了进程和线程的概念之后,说说协成。
协程:
协程,又称微线程。英文名Coroutine。
协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
用yield来实现传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待。
#!/usr/bin/env python
# -*- coding:utf-8 -*- import time def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('Consume running %s...' % n)
time.sleep(1) #遇到阻塞到produce执行
r = '200 OK' def produce(c):
c.next() #启动迭代器
n = 0
while n < 5:
n = n + 1
print('[Produce] running %s...' % n)
r = c.send(n) #到consumer中执行
print('[Consumer] return: %s' % r)
c.close() if __name__=='__main__':
c = consumer() #迭代器
produce(c) 执行结果:
[Produce] running 1...
Consume running 1...
[Consumer] return: 200 OK
[Produce] running 2...
Consume running 2...
[Consumer] return: 200 OK
[Produce] running 3...
Consume running 3...
[Consumer] return: 200 OK
[Produce] running 4...
Consume running 4...
[Consumer] return: 200 OK
[Produce] running 5...
Consume running 5...
[Consumer] return: 200 OK
其实python有个模块封装了协程功能,greenlet.来看代码。
#!/usr/bin/env python
# -*- coding:utf-8 -*- #封装好的协成
from greenlet import greenlet def test1():
print "test1:",11
gr2.switch()
print "test1:",12
gr2.switch() def test2():
print "test2:",13
gr1.switch()
print "test2:",14 gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch() 执行结果:
test1: 11
test2: 13
test1: 12
test2: 14
这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个自动切换比greenlet更强大的gevent。
其原理是当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。直接上代码
#!/usr/bin/env python
# -*- coding:utf-8 -*- #协成的自动切换
import gevent
import time def func1():
print('\033[31;1m 正在执行 111...\033[0m')
gevent.sleep(2)
print('\033[31;1m 正在执行 444...\033[0m') def func2():
print('\033[32;1m 正在执行 222...\033[0m')
gevent.sleep(3) #阻塞3秒,所以自动切换到func1,执行完func1后 再切换回来
print('\033[32;1m 正在执行 333...\033[0m') start_time = time.time()
gevent.joinall([
gevent.spawn(func1),
gevent.spawn(func2),
# gevent.spawn(func3),
])
end_time = time.time() #程序总共花费3秒执行
print "spend",(end_time-start_time),"second" 执行结果:
正在执行 111...
正在执行 222...
正在执行 444...
正在执行 333...
总耗时:
spend 3.00936698914 second
下面我们用greenlet来实现一个socket多线程处理数据的功能。不过需要安装一个monkey补丁,请自行安装吧。
client端:
#!/usr/bin/env python
# -*- coding:utf-8 -*- from socket import * ADDR, PORT = 'localhost', 8001
client = socket(AF_INET,SOCK_STREAM)
client.connect((ADDR, PORT)) while 1:
cmd = raw_input('>>:').strip()
if len(cmd) == 0: continue
client.send(cmd)
data = client.recv(1024)
print data
#print('Received', repr(data)) client.close()
server端:
#!/usr/bin/env python
# -*- coding:utf-8 -*- import sys
import socket
import gevent
from gevent import monkey
monkey.patch_all() def server(port):
sock = socket.socket()
sock.bind(('127.0.0.1', port))
sock.listen(500)
while 1:
conn, addr = sock.accept()
#handle_request(conn)
gevent.spawn(handle_request, conn) def handle_request(conn):
try:
while 1:
data = conn.recv(1024)
if not data:
break
print("recv:",data)
conn.send(data) except Exception as ex:
print(ex)
finally:
conn.close() if __name__ == '__main__':
server(8001)
以上代码可以自行多开几个客户端,然后执行看看,是不是很酷,无论客户端输入什么,服务端都能实时接收到。
OVER!
python中协程的更多相关文章
- 深入理解Python中协程的应用机制: 使用纯Python来实现一个操作系统吧!!
本文参考:http://www.dabeaz.com/coroutines/ 作者:David Beazley 缘起: 本人最近在学习python的协程.偶然发现了David Beazley的co ...
- python中协程实现的本质以及两个封装协程模块greenle、gevent
协程 协程,又称微线程,纤程.英文名Coroutine. 协程是啥 协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源). 为啥说它是一个执行单元,因为 ...
- Python中协程的实现
通过关键字yield,可以从生成器中产生值,并返回.我们可以将生成器作为一个生产者来使用. 在协程中,通过使用关键字yield,还可以让具有yield的程序接收值.此时函数作为消费者,消费我们传入(s ...
- python中协程的使用示例
例子1 把字符串分割为列表 def line_splitter( delimiter = None ): print( 'ready to split' ) result = None while T ...
- Python中协程Event()函数
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法wait.clear.set 事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 e ...
- python gevent 协程
简介 没有切换开销.因为子程序切换不是线程切换,而是由程序自身控制,没有线程切换的开销,因此执行效率高, 不需要锁机制.因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断 ...
- {python之协程}一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二
python之协程 阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本 ...
- 【Python】协程
协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在 ...
- Python之协程(coroutine)
Python之协程(coroutine) 标签(空格分隔): Python进阶 coroutine和generator的区别 generator是数据的产生者.即它pull data 通过 itera ...
随机推荐
- BZOJ1129 : [POI2008]Per
枚举LCP,假设前$i-1$个都相同.那么后面$n-i$个数可以随意排列,第$i$个位置可以填的方案数为后面小于$a_i$的数字个数,树状数组维护. 同时为了保证本质不同,方案数需要除以每个数字的个数 ...
- Python for Infomatics 第13章 网页服务四(译)
这几天因为其他事务,打断了自己的学习计划,今天继续我的翻译,避免又中途而废. 注:文章原文为Dr. Charles Severance 的 <Python for Informatics> ...
- WebRTC通信流程
WebRTC是HTML5支持的重要特性之一,有了它,不再需要借助音视频相关的客户端,直接通过浏览器的Web页面就可以实现音视频对聊功能.而且WebRTC项目是开源的,我们可以借助WebRTC源码快速构 ...
- 二分图&网络流&最小割等问题的总结
二分图基础: 最大匹配:匈牙利算法 最小点覆盖=最大匹配 最小边覆盖=总节点数-最大匹配 最大独立集=点数-最大匹配 网络流: 技巧: 1.拆点为边,即一个点有限制,可将其转化为边 BZOJ1066, ...
- 弹出popwindow 背景变暗
先看下效果图吧 代码如下 package com.example.administrator.popwindowdemo.view; import android.app.Activity; impo ...
- MongooseHelper
/** * Created by lbc on 2016/11/16. */var mongoose=require("mongoose");var db=mongoose.con ...
- 客户端用javascript获取文件大小
客户端用javascript获取文件大小 1 ie实现代码如下: <script type="text/javascript" language="javascri ...
- Android Activity task 相关属性
所谓的 task ,是指用户完成某一项任务时与之交互的一组 Activity.比如用户要向开发者汇报 bug,先打开程序主页,然后打开关于页面,再点击报告 bug 按钮,打开编辑邮件页面.当前这三个 ...
- Mac环境下Octopress个人博客搭建
一直想弄一个漂亮一点的个人博客,之前一直用的博客园,对主页的能自定义内容实在不满意,终于下定决定,找到了Octopress这个适合我的解决方案,以下过程都是自己一步一步记录下来的,希望对大家有帮助. ...
- java 过滤器Filter
一.首先在web.xml里进行拦截过滤 <filter> <filter-name>platformServiceAgreementFilter</filt ...