python -- 异步编程

我们在生产中,常用的处理任务模型有三种:
  单线程
  多线程
  异步(单线程内,串行,特点是遇到阻塞(或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")
 5     while True:
 6         new_baozi = yield
 7         print("[%s] is eating baozi %s " %(name,new_baozi))
 8         # time.sleep(1)  #在yield里面并没有实现阻塞切换
 9
10 def producer():
11     c = con1.__next__()
12     c = con2.__next__()
13     n = 0
14     while n < 5:
15         n += 1
16         print("\033[32;1m [producer]\033[0m is making baozi %s " % n)
17         con1.send(n)
18         con2.send(n)     #用send可以想yield发送数据
19
20 if __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)是不行的
11     print("\033[32;1m func1函数的第2部分... \033[0m")
12
13 def func2():
14     print("\033[31;1m func2函数的第1部分... \033[0m")
15     gevent.sleep(1)
16     # time.sleep(1)
17     print("\033[31;1m func2函数的第2部分... \033[0m")
18
19 def func3():
20     print("\033[34;1m func3函数的第1部分... \033[0m")
21     gevent.sleep(3)
22     # time.sleep(3)
23     print("\033[34;1m func3函数的第2部分... \033[0m")
24
25
26 if __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
 7 import gevent
 8 from urllib.request import urlopen
 9
10 def f(url):
11     print("GET: %s " %url)
12     resp = urlopen(url)
13     data = resp.read()
14     print("%s bytes received from %s " %(len(data),url))
15
16 if __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
 8 def server(port):
 9     s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
10     s.bind((‘0.0.0.0‘,port))
11     s.listen(5000)
12
13     while True:
14         # print("\033[32;1m server is waiting... \033[0m")
15         print(" server is waiting... ")
16         conn, addr = s.accept()
17         gevent.spawn(handle_request,conn)
18
19 def handle_request(conn):
20     try:
21         while True:
22             data = conn.recv(1024)
23             print("recvied:",data.decode(‘utf8‘))
24             conn.send(data)
25             if not data:
26                 conn.shutdown(socket.SHUT_WR)
27
28     except Exception as ex:
29         print(ex)
30
31     finally:
32         conn.close()
33
34
35 if __name__ == ‘__main__‘:
36     server(9999)

server

 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)
16         print("Received:",data.decode(‘utf8‘))
17
18     sk.close()
19
20 if __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)
24
25     thread_list = []
26     for i in range(2000):
27         t = threading.Thread(target=run,args=[i,])
28         t.start()
29         thread_list.append(t)
30
31     for thread in thread_list:
32         thread.join()

client

时间: 2024-10-16 07:27:49

python -- 异步编程的相关文章

&lt;史上最强&gt;深入理解 Python 异步编程(上)

前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知道如何使用 Tornado.Twisted.Gevent 这类异步框架上,出现各种古怪的问题难以解决.而且使用了异步框架的部分同学,由于用法不对,感觉它并没牛逼到哪里去,所以很多同学做 Web 后端服务时还是采用 Flask.Django等传统的非异步框架. 从上两届 PyCon 技术大会看来,异步编程已经成了 Python 生态下一阶段的主旋律.如新兴的 Go.Rust.

深入理解 Python 异步编程(上)

http://python.jobbole.com/88291/ 前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知道如何使用 Tornado.Twisted.Gevent 这类异步框架上,出现各种古怪的问题难以解决.而且使用了异步框架的部分同学,由于用法不对,感觉它并没牛逼到哪里去,所以很多同学做 Web 后端服务时还是采用 Flask.Django等传统的非异步框架. 从上两届 PyCon 技术大会看来,异步编程已经成

这篇文章讲得精彩-深入理解 Python 异步编程(上)!

可惜,二和三现在还没有出来~ ~~~~~~~~~~~~~~~~~~~~~~~~~ http://python.jobbole.com/88291/ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 在Python 3.3 引入yield from新语法之后,就不再推荐用yield去做协程.全都使用yield from由于其双向通道的功能,可以让我们在协程间随心所欲地传递数据. 4.5.3 yield from改进协程总结 用yield from改进基于生成器的协程,代码抽象程度更高.使业务

python异步编程--回调模型(selectors模块)

目录 0. 参考地址 1. 前言 2. 核心类 3. SelectSelector核心函数代码分析 3.1 注册 3.2 注销 3.3 查询 4. 别名 5. 总结 6. 代码报错问题 1. 文件描述符数量 2. 监听列表是否可以为空 7. 关系图 0. 参考地址 基本介绍 https://www.cnblogs.com/yinheyi/p/8127871.html 实验演示 https://www.cnblogs.com/xybaby/p/6406191.html#_label_2 详细讲解

(转)python异步编程--回调模型(selectors模块)

原文:https://www.cnblogs.com/zzzlw/p/9384308.html#top 目录 0. 参考地址 1. 前言 2. 核心类 3. SelectSelector核心函数代码分析 3.1 注册 3.2 注销 3.3 查询 4. 别名 5. 总结 6. 代码报错问题 1. 文件描述符数量 2. 监听列表是否可以为空 7. 关系图 0. 参考地址 基本介绍 https://www.cnblogs.com/yinheyi/p/8127871.html实验演示 https://w

python异步编程模块asyncio学习(二)

尽管asyncio应用通常作为单线程运行,不过仍被构建为并发应用.由于I/O以及其他外部事件的延迟和中断,每个协程或任务可能按一种不可预知的顺序执行.为了支持安全的并发执行,asyncio包含了threading和multiprocessing模块中的一些底层原语的实现. 锁(LOCK) 锁可以用来保护对一个共享资源的访问.只有锁的持有者可以使用这个资源.如果有多个请求要的到这个锁,那么其将会阻塞,以保证一次只有一个持有者. 看一个锁的例子: import asyncio from functo

[记录]python异步编程async/await实现

from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE import socket from types import coroutine from urllib.parse import urlparse @coroutine def until_readable(fileobj): yield fileobj, EVENT_READ @coroutine def until_writable(fileobj): yield

Python多线程,多进程,并行,并发,异步编程

Python并发与并行的新手指南:http://python.jobbole.com/81260/ Python 中的多线程,多进程,并发,并行,同步,通信:https://blog.csdn.net/timemachine119/article/details/54091323 python进阶笔记 thread 和 threading模块学习:https://www.cnblogs.com/forward-wang/p/5970640.html Python 中的多线程,多进程,并发,并行,

python 高性能编程之协程

用 greenlet 协程处理异步事件 自从 PyCon 2011 协程成为热点话题以来,我一直对此有着浓厚的兴趣.为了异步,我们曾使用多线程编程.然而线程在有着 GIL 的 Python 中带来的性能瓶颈和多线程编程的高出错风险,"协程 + 多进程"的组合渐渐被认为是未来发展的方向.技术容易更新,思维转变却需要一个过渡.我之前在异步事件处理方面已经习惯了回调 + 多线程的思维方式,转换到协程还非常的不适应.这几天我非常艰难地查阅了一些资料并思考,得出了一个可能并不可靠的总结.尽管这个