1.协程初识,greenlet模块java
2.gevent模块(须要pip安装)python
一.协程初识,greenlet模块:nginx
协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序本身控制调度的。数据库
greenlet模块要本身用pip安装编程
#协程: #本质上是一个线程 #可以在多个任务之间切换来节省一些IO时间 #协程中任务之间的切换也消耗时间,可是开销远远小于进程线程之间的切换 #协程的意义: #在遇到IO操做的时候,切换到另一个任务 #规避以前任务的IO时间,来提升cpu的利用率 #在实际工做中会采用:进程+线程+协程,来提升代码的并发效果 #进程是cpu核数+1,线程是cpu核数*5,每一个线程中协程最多能够起500个 #好比: #发送了一个网页请求后,在网络延时,等待网页响应的时间(等待IO),就能够用协程去切换任务利用等待的时间,继续发送多个网页请求,从而提升效率 #进程5,线程20,协程500 = 总共能够有50000个协程:一台4c的机器最多能够接收的并发数 #数据库,负载均衡,让不少个请求,平均分摊给各个服务器 #nginx组件 大型互联网公司会用到,就是用来帮你分发任务的,并发最大承载量就是50000,用的就是协程机制 #通常状况下就是根据这个规则,上下浮动 #协程的调度是由gevent完成的,而进程和线程的调度是cpu完成的 #greenlet之后都不怎么样,协程仍是主要用gevent #真正的协程模块就是使用greenlet完成的切换 from greenlet import greenlet def eat(): print('eating') g2.switch() #切换到g2运行g2,切记录当前g1运行的位置,切换后,若是g2那没切换回来,后面这句 print('eat end') 就不会运行了 print('eat end') def play(): print('playing') g1.switch() print('play end') g1 = greenlet(eat) g2 = greenlet(play) g1.switch() # def play2(): # print('playing') # g1.switch() # print('play end') # # def ch(): # play2.__name__ = 'play3' # ch() # print(play2.__name__)
#协程
#因为cpython解释器中的GIL缘由,致使python中多线程被弱化了,并且切换多个线程之间也要时间开销
#因此就出现了协程,协程的切换效率更快,把1个线程的做用发挥到了极致,提升1个cpu的利用率。减小线程的时间开销
#java里也有协程,可是没有这么被重视json
二.gevent模块服务器
安装:pip3 install gevent网络
Gevent 是一个第三方库,能够轻松经过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet所有运行在主程序操做系统进程的内部,但它们被协做式地调度。多线程
#切记 from gevent import monkey;monkey.patch_all() 这句话必定要写在想更改的模块前面并发
#GKX #gevent模块 join,joinall,value,spawn #协程适用于网络延迟的时候,也就是适合作爬虫的时候 ,或者socket链接的时候。代码若是没有高IO,不必用协程 #gevent只会识别它认识的IO操做 from gevent import monkey;monkey.patch_all() #monkey这句话必定要写在想更改的模块前面 import time #经过猴子补丁,让sleep不阻塞 import gevent import threading # 用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉. # 这样咱们在后面使用socket的时候能够跟日常同样使用,无需修改任何代码,可是它变成非阻塞的了. from greenlet import greenlet def eat(): print(threading.current_thread().getName()) #>>DummyThread-1 dummy(仿制品) print('eating') time.sleep(1) #使用猴子补丁后,至关于 gevent.sleep # gevent.sleep(1) print('eat end') def play(): print(threading.current_thread().getName()) print('playing') # time.sleep(1) gevent.sleep(1) print('play end') g1 = gevent.spawn(eat) #开启一个协程, spawn(大量生产意思) g2 = gevent.spawn(play) g1.join() #让线程等待协程的结果,若是没有join,线程执行完毕后直接关闭,等不到协程的结果 g2.join() print('11111111111111') #遇到sleep后会非阻塞,先执行其余任务,而后再同时来sleep1秒来打印任务 #有了gevent和猴子补丁,只要把函数注册进 gevent模块里,就可使用协程了,其余都不用管 #当遇到IO会自动帮你切换到其余任务,最后再一块儿共享,全部任务的IO操做 #遇到IO—执行其余任务—其余任务执行到也遇到了IO——继续执行其余任务—都遇到了IO—在IO之间来回切换,看谁解除了IO,立刻继续运行 #从而把等待IO的时间,用来执行任务。而后共享IO时间,达到提升效率的目的 #咱们能够不用理会gevent的执行过程,注册完线程运行等待结果就行了
from gevent import monkey;monkey.patch_all() #monkey这句话必定要写在想更改的模块前面 import time #经过猴子补丁,让sleep不阻塞 import gevent import threading #同步和异步 def task(): time.sleep(0.5) print('12345') def sync(): for i in range(10): task() def a_sync(): g_lst = [] for i in range(10): g = gevent.spawn(task) g_lst.append(g) gevent.joinall(g_lst) # == for g in g_lst:g.join() sync() a_sync()
# monkey patch指的是在运行时动态替换,通常是在startup的时候. # 用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉. # 这样咱们在后面使用socket的时候能够跟日常同样使用,无需修改任何代码,可是它变成非阻塞的了. # 以前作的一个游戏服务器,不少地方用的import json,后来发现ujson比自带json快了N倍, # 因而问题来了,难道几十个文件要一个个把import json改为import ujson as json吗? # 其实只须要在进程startup的地方monkey patch就好了.是影响整个进程空间的.同一进程空间中一个module只会被运行一次. # 下面是代码: # import json # import ujson # # def monkey_patch_json(): # json.__name__ = 'ujson' # json.dumps = ujson.dumps # json.loads = ujson.loads # # monkey_patch_json() # # print # 'main.py', json.__name__ # import sub # # # import json # print 'sub.py',json.__name__ # 最后,注意不能单纯的json = ujson来替换.
用gevent模块实现的socket服务端的并发:
from gevent import monkey;monkey.patch_all()
from gevent import monkey;monkey.patch_all() import gevent def func(conn): conn.send(b'hello') msg = conn.recv(1024).decode('utf8') print(msg) conn.close() #关闭若是放在循环里,就立刻关闭了,没有链接的意义 while True: conn, addr = sk.accept() #在这里阻塞,一直监听,一旦有client链接,立刻把conn扔给一个并发。而后继续循环,阻塞 g = gevent.spawn(func,conn) sk.close()
import time import socket sk = socket.socket() sk.connect(('127.0.0.1',8080)) print(sk.recv(1024)) msg = input('>>>> ').encode('utf8') sk.send(msg) sk.close()