前言:
搜狗输入法输入协程,出来的是“携程”,我也是醉了。
正文:
- 进程是资源分配的单位
- 线程是CPU调度的单位
- 协程被称为微线程,是比线程更小的执行单元
(先记住,具体怎么讲,我也不知道)
假设现在有三个线程,轮流占用CPU,具体怎么轮转是由CPU的调度算法决定的,不是开发者决定的,因为轮转的特别快,不可能让A从头执行到尾,然后再换B执行。。。为了执行的顺利,任何一个线程在占用CPU之前都要还原上次占用结束时的数据(简单来说如果是累加,1+…+100,加到50的时候,被切出去了,这是要把结果保存起来,下次轮转到了把结果取出来,继续累加)。
以上,线程在轮转的时候,需要保存数据,占用资源,而协程,相比之下占用资源远小于线程。
生成器版
import time
# 生成器
def A():
while True:
print('----A----')
yield
time.sleep(0.5)
def B(c):
while True:
print('----B----')
# python 2.7 是 c.next()
# python 3.x 是 next(c)
next(c)
time.sleep(0.5)
if __name__ == '__main__':
a = A()
B(a)
这里A是个生成器,结果是:
我们可以这样理解,函数A,B 两个函数在轮流占用CPU,函数的切换要比线程,进程的切换要保存的东西少了很多。所以占用资源要少了很多。
利用生成器是不是感觉比线程要麻烦一些呢?
别着急,看下面的内容。
greenlet版本
想简单就得做点准备工作
命令行
下载greenlet库。
from greenlet import *
import time
def task1():
while True:
print('------task1------')
grt2.switch()
time.sleep(0.5)
def task2():
while True:
print('------task2------')
grt1.switch()
time.sleep(0.5)
if __name__ == '__main__':
grt1 = greenlet(task1)
grt2 = greenlet(task2)
grt1.switch()
结果为:
从这个例子中我们可以看出,具体切换函数的时候是我们开发者在控制着的,而不像在使用线程,进程的时候具体切换任务是CPU的调度算法决定的。
当然有时候我们不需要关心切换问题,完全可以交给程序去运行。这时候就有个更强大的库了。叫做gevent。
gevent版本
下载库
代码:
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
def main():
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
if __name__ == '__main__':
main()
结果:
g1,g2,g3,是三个协程,我们看着好像是顺序执行一样。
别着急继续往下看
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
#这里添加了一句话,注意不是time.sleep
gevent.sleep(0.1)
def main():
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
if __name__ == '__main__':
main()
生成器和greenlet是开发者手动切换协程,gevent如果一个协程遇到一个耗时操作的时候,就会自动切换到另一个协程。
协程经常使用在IO密集型程序中。IO就是频繁耗时操作。gevent强大就强大在这里。
下面我们看一个利用gevent完成的TCP服务器:
#这里我试过了不能用 from gevent import *
from gevent import socket, monkey, spawn, getcurrent
#在执行这句话的时候,编译器自己把下面的代码就修改成了自己可以识别的耗时操作。
#以至于到了耗时就可以自动切换协程。
monkey.patch_all()
conns = {}
addrs = {}
def handle_request(conn):
print(getcurrent())
while True:
data = conn.recv(1024)
if data:
print("%s发来数据:%s" % (str(addrs[conn.fileno]), data))
conn.send(data)
else:
print("%s断开连接" % str(addrs[conn.fileno]))
del conns[conn.fileno]
del addrs[conn.fileno]
conn.close()
break
def server(port):
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', port))
s.listen(5)
while True:
print('等待新连接')
cli, addr = s.accept()
conns[cli.fileno] = cli
addrs[cli.fileno] = addr
spawn(handle_request, cli)
if __name__ == '__main__':
server(7788)
结果:
协程,到此结束
后面一节开始学习正则表达式。
例子下载:
下载地址