协程python
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。
如:

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 #进程之间的通信
5 from multiprocessing import Process,Queue
6
7 def f(qq):
8 #在子进程设置值,本质上是子进程pickle数据序列化到公共的地方
9 qq.put(['hello',None,123])
10
11
12 if __name__ == '__main__':
13 q = Queue()
14 t = Process(target=f,args=(q,))
15 t.start()
16 #从父进程中取出来,本质上是父进程pickle从公共的地方把数据反序列化出来
17 print q.get()
18 t.join()

3、新的线程很容易被创建,但是新的进程需要对其父进程进行一次克隆
4、一个线程可以操作和控制同一个进程里的其他线程,但进程只能操作其子进程。
明白了进程和线程的概念之后,说说协成。
协程:
协程,又称微线程。英文名Coroutine。
协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

用yield来实现传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待。

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 import time
5
6 def consumer():
7 r = ''
8 while True:
9 n = yield r
10 if not n:
11 return
12 print('Consume running %s...' % n)
13 time.sleep(1) #遇到阻塞到produce执行
14 r = '200 OK'
15
16 def produce(c):
17 c.next() #启动迭代器
18 n = 0
19 while n < 5:
20 n = n + 1
21 print('[Produce] running %s...' % n)
22 r = c.send(n) #到consumer中执行
23 print('[Consumer] return: %s' % r)
24 c.close()
25
26 if __name__=='__main__':
27 c = consumer() #迭代器
28 produce(c)
29
30 执行结果:
31 [Produce] running 1...
32 Consume running 1...
33 [Consumer] return: 200 OK
34 [Produce] running 2...
35 Consume running 2...
36 [Consumer] return: 200 OK
37 [Produce] running 3...
38 Consume running 3...
39 [Consumer] return: 200 OK
40 [Produce] running 4...
41 Consume running 4...
42 [Consumer] return: 200 OK
43 [Produce] running 5...
44 Consume running 5...
45 [Consumer] return: 200 OK

其实python有个模块封装了协程功能,greenlet.来看代码。

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 #封装好的协成
5 from greenlet import greenlet
6
7 def test1():
8 print "test1:",11
9 gr2.switch()
10 print "test1:",12
11 gr2.switch()
12
13 def test2():
14 print "test2:",13
15 gr1.switch()
16 print "test2:",14
17
18
19 gr1 = greenlet(test1)
20 gr2 = greenlet(test2)
21 gr1.switch()
22
23 执行结果:
24 test1: 11
25 test2: 13
26 test1: 12
27 test2: 14

这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个自动切换比greenlet更强大的gevent。
其原理是当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。直接上代码

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 #协成的自动切换
5 import gevent
6 import time
7
8 def func1():
9 print('\033[31;1m 正在执行 111...\033[0m')
10 gevent.sleep(2)
11 print('\033[31;1m 正在执行 444...\033[0m')
12
13
14 def func2():
15 print('\033[32;1m 正在执行 222...\033[0m')
16 gevent.sleep(3) #阻塞3秒,所以自动切换到func1,执行完func1后 再切换回来
17 print('\033[32;1m 正在执行 333...\033[0m')
18
19 start_time = time.time()
20 gevent.joinall([
21 gevent.spawn(func1),
22 gevent.spawn(func2),
23 # gevent.spawn(func3),
24 ])
25 end_time = time.time()
26
27 #程序总共花费3秒执行
28 print "spend",(end_time-start_time),"second"
29
30 执行结果:
31 正在执行 111...
32 正在执行 222...
33 正在执行 444...
34 正在执行 333...
35 总耗时:
36 spend 3.00936698914 second

下面我们用greenlet来实现一个socket多线程处理数据的功能。不过需要安装一个monkey补丁,请自行安装吧。
client端:

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 from socket import *
6
7 ADDR, PORT = 'localhost', 8001
8 client = socket(AF_INET,SOCK_STREAM)
9 client.connect((ADDR, PORT))
10
11 while 1:
12 cmd = raw_input('>>:').strip()
13 if len(cmd) == 0: continue
14 client.send(cmd)
15 data = client.recv(1024)
16 print data
17 #print('Received', repr(data))
18
19 client.close()

server端:

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4
5 import sys
6 import socket
7 import gevent
8 from gevent import monkey
9 monkey.patch_all()
10
11 def server(port):
12 sock = socket.socket()
13 sock.bind(('127.0.0.1', port))
14 sock.listen(500)
15 while 1:
16 conn, addr = sock.accept()
17 #handle_request(conn)
18 gevent.spawn(handle_request, conn)
19
20
21 def handle_request(conn):
22 try:
23 while 1:
24 data = conn.recv(1024)
25 if not data:
26 break
27 print("recv:",data)
28 conn.send(data)
29
30 except Exception as ex:
31 print(ex)
32 finally:
33 conn.close()
34
35 if __name__ == '__main__':
36 server(8001)

以上代码可以自行多开几个客户端,然后执行看看,是不是很酷,无论客户端输入什么,服务端都能实时接收到。
OVER!
协程python的更多相关文章
- Python—进程、线程、协程
一.线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 方法: ...
- Python之路【第七篇】:线程、进程和协程
Python之路[第七篇]:线程.进程和协程 Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1 2 3 4 5 6 7 8 9 10 11 12 1 ...
- python中的协程及实现
1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...
- 从yield 到yield from再到python协程
yield 关键字 def fib(): a, b = 0, 1 while 1: yield b a, b = b, a+b yield 是在:PEP 255 -- Simple Generator ...
- Python 实现协程
协程的概念 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程.(其实并没有说明白~) 我觉得单说协程,比较抽象,如果对线程有一定了解的话,应该就比较 ...
- python入门20180717-迭代器、生成器和协程
迭代器.生成器和协程 python中任意的对象,只要它定义了可以返回一个迭代器的__iter__方法,或者支持下标索引的_getitem_方法,那么它就是一个可迭代对象. 可迭代的对象不一定就是迭代器 ...
- Python进程、线程、协程及IO多路复用
详情戳击下方链接 Python之进程.线程.协程 python之IO多路复用
- Python与Golang协程异同
背景知识 这里先给出一些常用的知识点简要说明,以便理解后面的文章内容. 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定 ...
- 这篇文章揭开python进程、线程、协程神秘的面纱
1.概念 [关注公众号"轻松学编程"了解更多. 回复"协程"获取本文源代码.] 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务. 一个CPU,在 ...
随机推荐
- TFS2012 服务器安装
配置: 华硕Z97-A I7 4790K 2*2T 4*8G 操作系统: Win2012 标准 SN: DBGBW-NPF86-BJVTX-K3WKJ-MTB6V http://dinghuqiang ...
- nodejs get/request
灌水评论示例: var http = require('http'); var querystring = require('querystring'); var postData = queryst ...
- JavaScript Patterns 5.1 Namespace Pattern
global namespace object // global object var MYAPP = {}; // constructors MYAPP.Parent = function() { ...
- Linux简介及常用命令使用1--linux环境搭建
1.先安装:VMWare10软件 VMware Workstation是一款功能强大的虚拟机软件,可以使你在一台机器上同时运行二个或更多Windows.DOS.LINUX系统,并进行开发.测试.部署新 ...
- sql server 基础教程[温故而知新三]
子曰:“温故而知新,可以为师矣.”孔子说:“温习旧知识从而得知新的理解与体会,凭借这一点就可以成为老师了.“ 尤其是咱们搞程序的人,不管是不是全栈工程师,都是集十八般武艺于一身.不过有时候有些知识如果 ...
- 数据分页处理系列之三:Neo4j图数据分页处理
首先简单介绍下Neo4j,Neo4j是一个高性能的NOSQL图形数据库,它将结构化数据存储在网络上而不是表中,它是一个嵌入式的.基于磁盘的.具备完全的事务特性的Java持久化引擎,但是它将结构化数 ...
- ES6箭头函数(Arrow Functions)
ES6可以使用“箭头”(=>)定义函数,注意是函数,不要使用这种方式定义类(构造器). 一.语法 1. 具有一个参数的简单函数 var single = a => a single('he ...
- hive中的一种假NULL现象
使用hive时,我们偶尔会遇到这样的问题,当你将结果输出到屏幕时,查出的数据往往显示为null,但是当你将结果输出到文本时,却显示为空(即未填充),这是为什么呢? 在hive中有一种假NULL,它看起 ...
- 浅谈C++设计模式之抽象工厂(Abstract Factory)
抽象工厂(Abstract Factory)模式是为了提供一系列相关或相互依赖对象的接口.对象创建型模式的一种. 客户Client 抽象工厂接口AbstractFactory 抽象工厂的实现类Conc ...
- RCP常见问题
1.NoClassDefFoundError 解决办法: 查下依赖和插件启动情况 (manifest的requires和Import.osgi console插件启动情况) manifest的runt ...