进程:
数据隔离
数据不安全
操作系统级别
开销大
能利用多核
线程:
数据共享
数据不安全
操作系统级别
开销小
不能利用多核
对io操作的感知比较敏感(一些和文件操作相关的io只有操作系统能感知到)
协程:
数据共享
数据安全
操作系统不可见的
用户级别的
协程所有的切换都是基于用户的,只能根据用户级别能感知到的io操作
做切换来规避(socket, 请求网页...)
开销更小
不能利用多核
本质就是一条线程 多个任务在一条线程上来回切换,
利用协程这个概念实现的内容:来规避io操作即尽可能将io操作降低到最低
切换 并 规避io 的两个模块:
gevent 利用了 greenlet 底层模块完成的切换 + 自动规避io的功能
asyncio 利用了 yield 底层语法完成的切换 + 自动规避io的功能
tornado 异步的web框架
yield from - 其实是为了更好的实现协程
send
asyncio模块 基于python原生的协程的概念正式成立
特殊的在python中提供协程功能的关键字:async await
用户级别的协程有什么好处:
减轻了操作系统的负担
一条线程如果开了多条协程,那么给操作系统造成线程很忙的假象,这样能多争取一些时间片的时间
来被cpu执行,这样提高了程序的效率
2.gevent模块实现协程
from gevent import monkey monkey.patch_all() import gevent import time def func(): # 带有io操作的内容写在函数里,然后将函数交给gevent print('start func') time.sleep(1) print('end func') g1 = gevent.spawn(func) g2 = gevent.spawn(func) g3 = gevent.spawn(func) # g1.join() # 阻塞,直到协程g1任务执行结束 # g2.join() # g3.join() gevent.joinall([g1, g2, g3])
3.gevent实现socket并发的例子
server端
import socket print(socket.socket) # path_all()前打印一次 from gevent import monkey # gevent如何检测是否能规避某个模块的io操作 monkey.patch_all() import socket print(socket.socket) # path_all()之后打印一次,不一样则有效 import gevent def func(conn): while True: msg = conn.recv(1024).decode('utf-8') MSG = msg.upper() conn.send(MSG.encode('utf-8')) sk = socket.socket() sk.bind(('ip', port)) sk.listen() while True: conn, _ = sk.accept() gevent.spawn(func, conn)
client端
import socket import time from threading import Thread def func(sk): while True: sk.send(b'hello') msg = sk.recv(1024) print(msg) time.sleep(0.5) sk = socket.socket() sk.connect(('10.19.50.157', 9001)) for i in range(500): Thread(target=func, args=(sk, )).start()
4.asyncio模块实现协程
import asyncio async def func(name): print('start', name) # await + 可能会发生阻塞的方法 # await 必须写在一个async函数里 await asyncio.sleep(1) print('end') loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait([func('alex'), func('lgq')]))