python基础(协程创建,greenlet模块,gevent模块)

一、协程的概述

  1.1 定义:

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

    2. 线程属于内核级别的,即由操作系统控制调度,协程属于应用程序级别(而非操作系统)控制切换

  1.2 协程的本质:

    在一个线程中,多个任务遇到IO操作时,相互借切换轮流使用cpu,减小开销,增加执行效率
    多个协程的本质是一条线程(单线程),所以多个协程不能利用多核  

  1.3 比较:

    IO密集型应用:  多进程->多线程->事件驱动->协程
    CPU密集型应用: 多进程-->多线

二、 yield的协程机制

  生产者与消费者模型示例:

  
# 程序之间的相互转换调用-->协程

# 消费者
def consumer():
    while True:
        n = yield  # 每次获取一个值,挂起
        print(n)


# 生产者
def producer():
    g = consumer()
    next(g)
    for i in range(100):
        g.send(i)  # 每次传递一个值给到consumer


# 并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行
# 调用生产者函数,模拟协程
producer()
View Code

三、协程模块(greenlet)

  示例:

  
# 协程模块:

import time
from greenlet import greenlet  # 协程模块


def eat():
    print('start eating')
    g2.switch()  # 调用sleep方法,切换至sleep函数中执行
    time.sleep(1)
    print('end eating')
    g2.switch()


def sleep():
    print('start sleeping')
    g1.switch()  # 调用eat方法,切换至eat函数中执行
    time.sleep(1)
    print('end sleeping')


g1 = greenlet(eat)  # 实例化g1对象
g2 = greenlet(sleep)   # 实例化g2对象
g1.switch()  # 调用eat方法,切换至eat函数中执行
View Code

四、协程模块(gevent)

  
# 方式一:
import gevent


def eat():  # 协程任务 协程函数
    print('start eating')
    # 模块封装的方法(gevent.sleep()):能够阻塞模拟IO操作,切换任务
    gevent.sleep(1)  # 同time.sleep()
    print('end eating')


def sleep():  # 协程任务 协程函数
    print('start sleeping')
    gevent.sleep(1)
    print('end sleeping')


g1 = gevent.spawn(eat)
g2 = gevent.spawn(sleep)
# g1.join()   # 阻塞,直到g1任务执行完毕
# g2.join()   # 阻塞,直到g2任务执行完毕
gevent.joinall([g1, g2])  # g1.join() + g2.join()
View Code
  
# 方式二:

# 请求网页
url_dic = {
    '协程': 'http://www.cnblogs.com/Eva-J/articles/8324673.html',
    '线程': 'http://www.cnblogs.com/Eva-J/articles/8306047.html',
    '目录': 'https://www.cnblogs.com/Eva-J/p/7277026.html',
    '百度': 'http://www.baidu.com',
    'sogou': 'http://www.sogou.com',
    '4399': 'http://www.4399.com',
    '豆瓣': 'http://www.douban.com',
    'sina': 'http://www.sina.com.cn',
    '淘宝': 'http://www.taobao.com',
    'JD': 'http://www.JD.com'
}


from gevent import monkey; monkey.patch_all()  # 由monkey包装后的模块或者包都能识别程序的IO操作
import time
import gevent
from urllib.request import urlopen


# 获取网页
def get_html(name, url):
    ret = urlopen(url)  # 获取网页内容
    content = ret.read()  # 解码html网页
    with open(name, 'wb') as f:  # 写入文件
        f.write(content)


# 同步请求网页:
start = time.time()
for name in url_dic:
    get_html(name + '_sync.html', url_dic[name])
ret = time.time() - start
print('同步时间 :', ret)  # 同步时间 : 3.444990396499634


# 异步协程方式获取网页:
start = time.time()
g_l = []
for name in url_dic:
    g = gevent.spawn(get_html, name + '_async.html', url_dic[name])  # 实例化一个协程对象
    g_l.append(g)
gevent.joinall(g_l)  # 阻塞,等待所有任务执行完成
ret = time.time() - start
print('异步时间 :', ret)   # 异步时间 : 1.6289100646972656
协程实现异步的获取网页

五、协程实现高并发socket_server服务器

  
import socket
import gevent
from gevent import monkey

monkey.patch_all()  # 所有模块打补丁

sk = socket.socket()
sk.bind(('127.0.0.1', 9001))
sk.listen()


# 消息处理
def talk(conn):
    while True:
        msg = conn.recv(1024).decode('utf-8')
        conn.send(msg.upper().encode('utf-8'))


while True:
    conn, _ = sk.accept()
    gevent.spawn(talk, conn)



# 总结:
# 进程 高计算型的场景下使用进程
# 线程 对于IO操作的检测是更加全面且灵敏的
# 协程 能够检测到的io操作是有限的
server
  
import socket
from threading import Thread


def client(i):
    sk = socket.socket()
    sk.connect(('127.0.0.1', 9001))
    while True:
        sk.send(b'hello')
        print(sk.recv(1024))


# 创建500个线程
for i in range(500):
    Thread(target=client, args=(i,)).start()
client

 

posted @ 2019-04-19 17:47  Amorphous  阅读(146)  评论(0编辑  收藏  举报