首页 > 代码库 > Python 协程(gevent)

Python 协程(gevent)

协程,又叫微线程,协程是一种用户态的轻量级线程

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

协程的好处:

  无需线程上下文切换的开销
  无需原子操作锁定及同步的开销
  方便切换控制流,简化编程模型
  高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:

  无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
  进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

用yield实现协程

 1 import time
 2 import queue
 3 
 4 def consumer(name):
 5     print("--->starting ...")
 6     while True:
 7         new_baozi = yield
 8         print("[%s] is eating baozi %s" % (name, new_baozi))
 9         # time.sleep(1)
10 
11 def producer():
12     next(con)
13     next(con2)
14     n = 0
15     while n < 5:
16         n += 1
17         print("\033[32;1m[producer]\033[0m is making baozi %s" % n)
18 
19         con.send(n)
20         con2.send(n)
21 
22 if __name__ == __main__:
23     con = consumer("c1")   #  创建一个生成器对象con
24     con2 = consumer("c2")  #  创建一个生成器对象con2
25     p = producer()         #  执行producer函数,p就是函数返回值

Gevent

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

需要先安装Gevent库

简单测试

 1 from greenlet import greenlet
 2 
 3 def test1():
 4     print(12)
 5     gr2.switch()   # 切换
 6     print(34)
 7     gr2.switch()
 8 
 9 def test2():
10     print(56)
11     gr1.switch()
12     print(78)
13 
14 gr1 = greenlet(test1)
15 gr2 = greenlet(test2)
16 
17 gr1.switch()
18 
19 ########## 输出 ##########
20 12
21 56
22 34
23 78

另一个例子

gevent.sleep() 模拟IO阻塞

import gevent
import time


def foo():
    print(Running in foo, time.ctime())
    gevent.sleep(1)  
    print(Explicit context switch to foo again, time.ctime())


def bar():
    print(Explicit context to bar, time.ctime())
    gevent.sleep(2)
    print(Implicit context switch back to bar, time.ctime())

gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
])

输出

Running in foo Thu Oct 20 10:52:58 2016
Explicit context to bar Thu Oct 20 10:52:58 2016
Explicit context switch to foo again Thu Oct 20 10:52:59 2016
Implicit context switch back to bar Thu Oct 20 10:53:00 2016

爬网页

 1 import gevent
 2 import time
 3 from gevent import monkey
 4 monkey.patch_all()
 5 from urllib.request import urlopen
 6 
 7 
 8 def f(url):
 9     print(GET: %s % url)
10     resp = urlopen(url)
11     data =http://www.mamicode.com/ resp.read()
12     print(%d bytes received from %s. % (len(data), url))
13 
14 l = [https://www.python.org/, https://www.yahoo.com/, https://github.com/]
15 start = time.time()
16 # for url in l:
17 #     f(url)
18 
19 # gevent.joinall([
20 #     gevent.spawn(f, ‘https://www.pthton.org/‘),
21 #     gevent.spawn(f, ‘https://www.yahoo.com/‘),
22 #     gevent.spawn(f, ‘https://github.com/‘),
23 # ])
24 
25 
26 gevent.joinall([
27     gevent.spawn(f, https://www.bilibili.com/),
28     gevent.spawn(f, http://weibo.com/),
29     gevent.spawn(f, http://www.qq.com/),
30 ])
31 
32 print(time.time() - start)

socket下的gevent

server

 1 import sys
 2 import socket
 3 import time
 4 import gevent
 5 
 6 from gevent import socket, monkey
 7 
 8 monkey.patch_all()
 9 
10 
11 def server(port):
12     s = socket.socket()
13     s.bind((0.0.0.0, port))
14     s.listen(500)
15 
16     while True:
17         conn, addr = s.accept()
18         gevent.spawn(handle_request, conn)
19 
20 
21 def handle_request(conn):
22     try:
23         while True:
24             data = http://www.mamicode.com/conn.recv(1024)
25             print("recv:", data)
26             conn.send(data)
27             if not data:
28                 conn.shutdown(socket.SHUT_WR)
29                 #break
30 
31     except Exception as  ex:
32         print(ex)
33     finally:
34         conn.close()
35 
36 
37 if __name__ == __main__:
38     server(8001)

clinent

 1 import socket
 2 
 3 HOST = localhost  # The remote host
 4 PORT = 8001  # The same port as used by the server
 5 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6 s.connect((HOST, PORT))
 7 
 8 
 9 while True:
10     msg = bytes(input(">>:"), encoding="utf8")
11     s.sendall(msg)
12     data = http://www.mamicode.com/s.recv(1024)
13     # print(data)
14     print(Received, str(data,utf8))
15 
16 s.close()

 

Python 协程(gevent)