设为首页 收藏本站
查看: 1266|回复: 0

[经验分享] python中协程

[复制链接]

尚未签到

发表于 2018-8-13 08:04:48 | 显示全部楼层 |阅读模式
  在引出协成概念之前先说说python的进程和线程。
  进程:
  进程是正在执行程序实例。执行程序的过程中,内核会讲程序代码载入虚拟内存,为程序变量分配空间,建立 bookkeeping 数据结构,来记录与进程有关的信息,

  比如进程>  进程结束的时候资源会释放出来,来让其他资源使用。我们可以把进程理解为一种容器,容器内的资源可多可少,但是在容器内的程序只能使用容器内的东西。因此启动
  进程的时候会比较慢,尤其是windows,尤其是多进程的时候(最好是在密集性运算的时候启动多进程)
  线程:
  一个进程中可以执行多个线程。多个线程共享进程内的资源。所以可以将线程可以看成是共享同一虚拟内存以及其他属性的进程。
  线程相对于进程的优势在于同一进程下的不同线程之间的数据共享更加容易。
  在说到线程的时候说说GIL(全局解释性锁 GLOBAL INTERPRETER LOCK),GIL 的存在是为了实现 Python 中对于共享资源访问的互斥。而且是非常霸道的解释器级别的互斥。在 GIL 的机制下,一个线程访问解释器之后,其他的线程就需要等待这个线程释放之后才可以访问。这种处理方法在单处理器下面并没有什么问题,单处理器的本质是串行执行的。但是再多处理器下面,这种方法会导致无法利用多核的优势。Python 的线程调度跟操作系统的进程调度类似,都属于抢占式的调度。一个进程执行了一定时间之后,发出一个信号,操作系统响应这个时钟中断(信号),开始进程调度。而在 Python 中,则通过软件模拟这种中断,来实现线程调度。比如:对全局的num做加到100的操作,可能在你加到11的时候,还没加完,则CPU就交给另一个线程处理,所以最后的结果可能比100会小或者比100会大。
  简单的说说进程和线程的几点关系
  1、启动一个进程至少会有一个线程
  2、修改主线程的数据会影响到子线程的数据,因为他们之间内存是共享的,修改主进程不会影响到子进程的数据,两个子进程之间是相互独立的,如果要实现子进程间的通信,可以利用中间件,比如multiprocessing的Queue。
  如:
DSC0000.gif

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呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
DSC0001.png

  用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 r10         if not n:11             return12         print('Consume running %s...' % n)13         time.sleep(1) #遇到阻塞到produce执行14         r = '200 OK'15 16 def produce(c):17     c.next() #启动迭代器18     n = 019     while n < 5:20         n = n + 121         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 OK34 [Produce] running 2...35 Consume running 2...36 [Consumer] return: 200 OK37 [Produce] running 3...38 Consume running 3...39 [Consumer] return: 200 OK40 [Produce] running 4...41 Consume running 4...42 [Consumer] return: 200 OK43 [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:",1211     gr2.switch()12 13 def test2():14     print "test2:",1315     gr1.switch()16     print "test2:",1417 18 19 gr1 = greenlet(test1)20 gr2 = greenlet(test2)21 gr1.switch()22 23 执行结果:24 test1: 1125 test2: 1326 test1: 1227 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: continue14     client.send(cmd)15     data = client.recv(1024)16     print data17     #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                 break27             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!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-550905-1-1.html 上篇帖子: python 元类 下篇帖子: PYTHON 多线程信号量
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表