Python 协程/异步IO/SelectPollEpoll异步IO与事件驱动

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 协程/异步IO/SelectPollEpoll异步IO与事件驱动相关的知识,希望对你有一定的参考价值。

 

1 Gevent 协程

协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程

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

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

 

协程的好处:

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

缺点:

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

Gevent 

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

__author__ = Administrator

import gevent

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

def bar():
    print(Explicit context to bar)
    gevent.sleep(1)
    print(Implicit context switch back to bar)

def ex():
    print(Explicit context to ex)
    gevent.sleep(1)
    print(Implicit context switch back to ex)

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

获取的值

Running in foo
Explicit context to bar
Explicit context to ex
Explicit context switch to foo again
Implicit context switch back to ex
Implicit context switch back to bar

 

遇到IO阻塞时会自动切换任务

from gevent import monkey; monkey.patch_all()
import gevent
from  urllib.request import urlopen

def f(url):
    print(GET: %s % url)
    resp = urlopen(url)
    data = resp.read()
    print(%d bytes received from %s. % (len(data), url))

gevent.joinall([
        gevent.spawn(f, https://www.python.org/),
        gevent.spawn(f, https://www.yahoo.com/),
        gevent.spawn(f, https://github.com/),
])

获取的值

GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
47381 bytes received from https://www.python.org/.
440972 bytes received from https://www.yahoo.com/.
25513 bytes received from https://github.com/.

Process finished with exit code 0

 

通过gevent实现单线程下的多socket并发 之前我们是用的socketserver 这个是低效的

技术分享
import socket

HOST = localhost    # The remote host
PORT = 8001           # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"),encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    #print(data)

    print(Received, repr(data))
s.close()
server
技术分享
import sys
import socket
import time
import gevent

from gevent import socket,monkey
monkey.patch_all()
def server(port):
    s = socket.socket()
    s.bind((0.0.0.0, port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli)
def handle_request(s):
    try:
        while True:
            data = s.recv(1024)
            print("recv:", data)
            s.send(data)
            if not data:
                s.shutdown(socket.SHUT_WR)

    except Exception as  ex:
        print(ex)
    finally:

        s.close()
if __name__ == __main__:
    server(8001)
client

 

以上是关于Python 协程/异步IO/SelectPollEpoll异步IO与事件驱动的主要内容,如果未能解决你的问题,请参考以下文章

Python协程异步IO

python爬虫--多任务异步协程, 快点,在快点......

Python协程&asyncio&异步编程

python协程和异步IO——IO多路复用

Python进阶协程和异步 IO

Python基础15 - 协程异步IO