greenlet和gevent模块

协程是用户态的线程,并非真正意义上的线程,

协程只有一个线程,看起来并发的效果是因为它利用了寄存器的上下文切换,

多线程和多进程比较消耗cpu资源,当遇到修改数据的时候,还会遇到死锁的问题。

协程是最大的发挥了cpu的单核能力,遇到io阻塞就切换,阻塞完成之后切换回来。

协程的好处:

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

缺点:

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

yield生成器实现协程的并发效果:

import time
"""
协程:是用户态的线程
yield单线程下实现并发效果
"""

#生产者
def producer(name):
    con1.__next__()
    con2.__next__()
    count = 0
    while count < 5:
        time.sleep(1)
        print("\033[31;1m[producer %s]\033[0m is making baozi..." % name) 
        con1.send(count)
        con2.send(count)
        count += 1

#消费者
def consumer(name):        
    print("准备开始吃包子...")
    while True:
        baozi = yield
        print("[%s]吃了包子[%s]" % (name, baozi))


if __name__ == '__main__':
    con1 = consumer("轩轩")
    con2 = consumer("壮壮")
    producer("小白")

  

greenlet代码示例:

from greenlet import greenlet


def homepage():
    print("34")
    gr2.switch()
    print("12")
    gr3.switch()

def bbs():
    print("84")
    gr3.switch()
    print("13")

def login():
    print("56")
    gr1.switch()
    print("--end--")




gr1 = greenlet(homepage)
gr2 = greenlet(bbs)
gr3 = greenlet(login)

  执行结果:

34
84
56
12
--end--

gevent模块是greentlet模块的简单化:

import gevent
"""
自动档的协程
自上而下从0开始计数,如果计数不大于sleep数,则跳过后面代码
否则,执行后面代码
"""

def run1():
    print("run1->1")
    gevent.sleep(2)
    print("run1->2")

def run2():
    print("run2->1")
    gevent.sleep(3)
    print("run2->2")

def run3():
    print("run3->1")
    gevent.sleep(1)
    print("run3->2")

def run4():
    print("run4->1")
    gevent.sleep(0)
    print("run4->2")

#由上而下依次执行
gevent.joinall([
    gevent.spawn(run1),
    gevent.spawn(run2),
    gevent.spawn(run3),
    gevent.spawn(run4),
])

  执行结果:

run1->1
run2->1
run3->1
run4->1
run4->2
run3->2
run1->2
run2->2

利用协程实现并发爬取网页内容:

import gevent, time

from gevent import monkey
monkey.patch_all()

from urllib import request
"""
利用协程,高效爬取网页
gevent模块默认不识别urllib的IO操作,
需要导入monkey给urllib里面的操作打上标记
"""
filename = "result.html"
#爬取网页内容到本地
def get_url_content(url):
    print("GET: %s" % url)
    res = request.urlopen(url)
    html = res.read()
    with open(filename, "wb") as f:
        f.write(html)
    print("%d bytes received from %s" % (len(html), url))
    print("----------------------")

url_list = [
    "https://www.python.org/",
    "https://github.com/",
    "https://www.bilibili.com/"
]

#启动时间
serial_start_time = time.time()
#串行的方式获取网页内容
for url in url_list:
    get_url_content(url)
#计算花费时间
print("serial cost:", time.time() - serial_start_time)


#启动时间
async_start_time = time.time()
#并行的方式获取网页内容
gevent.joinall([
    gevent.spawn(get_url_content, "https://www.python.org/"),
    gevent.spawn(get_url_content, "https://github.com"),
    gevent.spawn(get_url_content, "https://www.bilibili.com/"),
])
#计算花费时间
print("async cost:", time.time() - async_start_time)

  协程实现高并发socket服务器:

服务端:

import gevent
from gevent import monkey
monkey.patch_all()
import socket
"""
协程实现socket的高并发服务器
"""


def handle_data(conn):
    try:
        while True:
            data = conn.recv(1024)
            if not data:
                conn.shutdown(socket.SHUT_WR)
            conn.send(data.upper())
    except EXception as ex:
        print(ex)
    finally:
        conn.close()

def my_server(port):
    server = socket.socket()
    #绑定地址和端口 
    server.bind(("0.0.0.0", port))
    #开始监听
    server.listen(500)
    while True:
        #阻塞等待连接
        conn, addr = server.accept()
        #来数据了
        gevent.spawn(handle_data, conn)


if __name__ == '__main__':
    my_server(6666)

  客户端:

import socket
"""
协程实现socket高并发的客户端
"""


client = socket.socket()

client.connect(("localhost", 6666))


while True:
    input_data = input(">>:").strip()
    if not input_data:
        continue
    client.send(input_data.encode())
    data = client.recv(1024).decode()
    print(data)
client.close()

  

猜你喜欢

转载自www.cnblogs.com/ericbai/p/9130188.html