首页 / PYTHON / python -- 异步编程
python -- 异步编程
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了python -- 异步编程,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5470字,纯文字阅读大概需要8分钟。
内容图文
我们在生产中,常用的处理任务模型有三种:
单线程
多线程
异步(单线程内,串行,特点是遇到阻塞(或IO之类的)就切换到其他任务)
其中一般如果都符合要求,那么异步是最好的选择。
单线程:遇到阻塞整个程序都等待
多线程:以空间换取时间,且有时候伴随着数据安全问题(通常加锁来处理)
异步:在单个线程内,且是串行执行,但是一旦遇到阻塞(IO之类的),就会切换到线程内的其他任务(把IO操作交给操作系统处理)
当我们面对如下的环境时,事件驱动模型(异步模型)通常是一个好的选择(and):
1、程序中有许多任务
2、任务之间高度独立(因此它们不需要互相通信,或者等待彼此)
3、在等待事件到来时,某些任务会阻塞。
常用的异步IO模型:select、poll、Epoll (windows下只支持select)
nginx就是Epoll模型实现的。单线程,多进程(为了利用多核,单线程只能跑在单个cup核心上)
前面我们说了多线程与多进程,总结其特点就是:
单线程串行执行,遇到IO就阻塞,效率低。
多线程并发执行,遇到IO就切换(用空间换取执行时间),效率上去了,但是耗费资源,操作复杂。
针对以上问题,出现了一种新的替代品,协程。
协程
协程又名微线程,纤程。协程是一种用户态的轻量级线程(协程由用户切换,线程由cpu时间片控制切换)协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。跟yield过程差不多
协程的定义标准:
1、必须在一个单线程里面实现并发
2、修改共享数据不需要加锁(因为协程是串行的)
3、用户程序里自己保存多个控制流的上下文栈
4、一个协程遇到IO操作(阻塞也一样)就自动切换到其他协程
协程的好处:
1、无需线程上下文切换的开销
2、无需原子操作锁定及同步的开销
"原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
3、方便切换控制流,简化编程模型
4、高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
1、无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
2、进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
协程例子
1、用yield实现协程操作的例子:
1 import time 2 3 def consumer(name): 4 print("\033[32;1m ---> starting eating baozi... \033[0m") 5while True: 6 new_baozi = yield 7print("[%s] is eating baozi %s " %(name,new_baozi)) 8# time.sleep(1) #在yield里面并没有实现阻塞切换 910def producer(): 11 c = con1.__next__() 12 c = con2.__next__() 13 n = 0 14while n < 5: 15 n += 1 16print("\033[32;1m [producer]\033[0m is making baozi %s " % n) 17 con1.send(n) 18 con2.send(n) #用send可以想yield发送数据 1920if__name__ == ‘__main__‘: 21 con1 = consumer(‘c1‘) 22 con2 = consumer(‘c2‘) 23 p = producer()
执行结果
---> starting eating baozi... ---> starting eating baozi... [producer] is making baozi 1 [c1] is eating baozi 1 [c2] is eating baozi 1 [producer] is making baozi 2 [c1] is eating baozi 2 [c2] is eating baozi 2 [producer] is making baozi 3 [c1] is eating baozi 3 [c2] is eating baozi 3 [producer] is making baozi 4 [c1] is eating baozi 4 [c2] is eating baozi 4 [producer] is making baozi 5 [c1] is eating baozi 5 [c2] is eating baozi 5
2、使用gevent实现协程
1 # !/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import gevent 5 import time 6 7 def func1(): 8 print("\033[32;1m func1函数的第1部分... \033[0m") 9 gevent.sleep(2) 10# time.sleep(2) #用time.sleep(2)是不行的11print("\033[32;1m func1函数的第2部分... \033[0m") 1213def func2(): 14print("\033[31;1m func2函数的第1部分... \033[0m") 15 gevent.sleep(1) 16# time.sleep(1)17print("\033[31;1m func2函数的第2部分... \033[0m") 1819def func3(): 20print("\033[34;1m func3函数的第1部分... \033[0m") 21 gevent.sleep(3) 22# time.sleep(3)23print("\033[34;1m func3函数的第2部分... \033[0m") 242526if__name__ == ‘__main__‘: 27 gevent.joinall([ 28 gevent.spawn(func1), 29 gevent.spawn(func2), 30 gevent.spawn(func3), 31 ])
执行结果
func1函数的第1部分...
func2函数的第1部分...
func3函数的第1部分...
func2函数的第2部分...
func1函数的第2部分...
func3函数的第2部分...
3、协程结合urllib模块爬网站
1 # !/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 from gevent import monkey 5 monkey.patch_all() #遇到阻塞就切换全靠它(作用:把要用到的接口全部变为非阻塞模式) 6 7import gevent 8from urllib.request import urlopen 910def f(url): 11print("GET: %s " %url) 12 resp = urlopen(url) 13 data = resp.read() 14print("%s bytes received from %s " %(len(data),url)) 1516if__name__ == ‘__main__‘: 17 gevent.joinall([ 18 gevent.spawn(f, ‘http://www.cdu.edu.cn/‘), 19 gevent.spawn(f,‘https://www.python.org/‘), 20 gevent.spawn(f,‘https://www.jd.com/‘), 21 gevent.spawn(f,‘https://www.vip.com/‘) 22 ])
执行结果
GET: http://www.cdu.edu.cn/ GET: https://www.python.org/ GET: https://www.jd.com/ GET: https://www.vip.com/ 137584 bytes received from https://www.jd.com/ 69500 bytes received from https://www.vip.com/ 47984 bytes received from http://www.cdu.edu.cn/ 47695 bytes received from https://www.python.org/ Process finished with exit code 0
4、利用协程实现高并发服务器
1 # !/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import gevent 5 import socket 6 from gevent import monkey; monkey.patch_all() 7 8def server(port): 9 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 10 s.bind((‘0.0.0.0‘,port)) 11 s.listen(5000) 1213while True: 14# print("\033[32;1m server is waiting... \033[0m")15print(" server is waiting... ") 16 conn, addr = s.accept() 17 gevent.spawn(handle_request,conn) 1819def handle_request(conn): 20try: 21while True: 22 data = conn.recv(1024) 23print("recvied:",data.decode(‘utf8‘)) 24 conn.send(data) 25ifnot data: 26 conn.shutdown(socket.SHUT_WR) 2728except Exception as ex: 29print(ex) 3031finally: 32 conn.close() 333435if__name__ == ‘__main__‘: 36 server(9999)
1 # !/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import socket 5 import threading 6 7 def run(n): 8 ‘‘‘ 这里是启动多线程,然后每个线程死循环发包给服务器,测试协程服务器的高并发和稳定性 ‘‘‘ 9 while True: 10 # msg = input(">>:").strip() 11 # if len(msg) == 0:continue 12 # if msg == ‘q‘:break 13 msg = ‘hello %s‘ %n 14 sk.send(bytes(msg,‘utf8‘)) 15 data = sk.recv(1024) 16print("Received:",data.decode(‘utf8‘)) 1718 sk.close() 1920if__name__ == ‘__main__‘: 21 IP_Port = (‘127.0.0.1‘, 9999) 22 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 23 sk.connect(IP_Port) 2425 thread_list = [] 26for i in range(2000): 27 t = threading.Thread(target=run,args=[i,]) 28 t.start() 29 thread_list.append(t) 3031for thread in thread_list: 32 thread.join()
原文:http://www.cnblogs.com/xtsec/p/6985839.html
内容总结
以上是互联网集市为您收集整理的python -- 异步编程全部内容,希望文章能够帮你解决python -- 异步编程所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。