一、什么是协程?

是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

协程相比于线程切换效率更快了.

本质是线程

能够在多个任务之间切换来节省一些IO时间.

协程中任务之间的切换时间开销要远远小于进程线程之间的切换.

真正的协程模块就是使用greenlet完成切换的.

进程和协程的任务切换由操作系统完成.

协程任务之间的切换由程序完成.

 协程任务之间的切换由程序代码完成 , 只有遇到协程模块能识别的io操作的时候程序才会进行任务切换,实现并发的效果。

 

协程: 

能够在一个线程中实现并发效果的概念.

能够规避一些任务中的IO操作.

在任务的执行过程中,检测到IO就切换到其他任务.

 

在两个任务之间来回切换数据(简单的协程的概念)

# import time
# def consumer():
#     while True:
#         x = yield
#         time.sleep(1)
#         print('处理了数据 :',x)
#
# def producer():
#     c = consumer()#生成一个生成器对象
# next(c) #每次next返回一个值
# for i in range(10):
# time.sleep(1)
# print('生产了数据 :',i)
# c.send(i)
# producer()

输出结果:虽然实现了切换,但是没有节约IO时间.

D:\PycharmProjects\test\venv\Scripts\python.exe D:/parcharm/42.py
生产了数据 : 0
处理了数据 : 0
生产了数据 : 1
处理了数据 : 1
生产了数据 : 2
处理了数据 : 2
生产了数据 : 3
处理了数据 : 3
生产了数据 : 4
处理了数据 : 4
生产了数据 : 5
处理了数据 : 5
生产了数据 : 6
处理了数据 : 6
生产了数据 : 7
处理了数据 : 7
生产了数据 : 8
处理了数据 : 8
生产了数据 : 9
处理了数据 : 9

 

二 、安装 gevent和greenlet

Day 42 协程. IO 并发

 查看安装状态

 Day 42 协程. IO 并发

 

 真正的协程模块就是使用greenlet完成的切换

from greenlet import  greenlet
def eat():
    print("吃开始")
    g2.switch()
    print("吃完了")
    g2.switch()
def play():
    print("开始玩")
    g1.switch()
    print('玩完了')
g1 =greenlet(eat)
g2 =greenlet(play)
g1.switch()

  打印结果:

D:\PycharmProjects\test\venv\Scripts\python.exe D:/parcharm/44.py
吃开始
开始玩
吃完了
玩完了

  

Day 42 协程. IO 并发

 

 

协程能够在一个线程中实现并发效果的概念。

  能够规避一些任务中的IO操作

  在任务的执行过程中,检测到IO就切换到其他的任务.

 

三、爬虫的例子(正则有基础.)

请求过程中的IO等待

 

import requests #需要pip安装的。
from urllib.request import urlopen#内置的模块
url='http://www.baidu.com'
res1 =urlopen(url)
res2 = requests.get(url)
print(res1)
print(res2)

print(res1.read().decode('utf-8'))# 有格式的
print(res2.content.decode('utf-8'))#无格式的。

  

 

爬虫 (结果是一起显示出来的。)

gevent 用在爬虫和socket的编程中会用的到.

gevent是python的一个并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效.而且其中有个monkey类,

将现有基于Python线程直接转化为greenlet(类似于打patch).他和线程框架性能比高大概4倍(看下图,是gevent和paste的对比):

from gevent import  monkey;monkey.patch_all()
import gevent
from urllib.request import urlopen
def get_url(url):
    response = urlopen(url)
    content = response.read().decode('utf-8')
    return len(content)

g1 =gevent.spawn(get_url,'http://www.baidu.com')
g2 =gevent.spawn(get_url,'http://www.sohu.com')
g3 =gevent.spawn(get_url,'http://www.hao123.com')
g4 =gevent.spawn(get_url,'http://www.cnblog.com')
g5 =gevent.spawn(get_url,'http://www.cnblog.com')
gevent.joinall([g1,g2,g3,g4,g5])
print(g1.value)
print(g2.value)
print(g3.value)
print(g4.value)
print(g5.value)

  打印结果

 

D:\PycharmProjects\test\venv\Scripts\python.exe D:/parcharm/44.py
114439
185037
523375
1887
1887

协程实现socket的案例

协程间的切换是代码级别的, 

线程间切换时操作系统级别的。

 Server端

from gevent import monkey;monkey.patch_all()
import socket
import gevent
def talk(conn):
    conn.send(b'hello')
    print(conn.recv(1024).decode('utf-8'))
    conn.close()

sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
while True:
    conn,addr = sk.accept()
    gevent.spawn(talk,conn)
sk.close()

  client端

 

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
print(sk.recv(1024))
msg = input('>>>').encode('utf-8')
sk.send(msg)
sk.close()

  

四 IO模型.

blocking IO 阻塞IO 

nonblocking IO 非阻塞IO 

IO multiplexing  IO 多路复用

asynchronous IO 异步 IO

 

 

Blocking IO 阻塞IO

 

同步:同步提交一个任务之后要等待这个任务执行完毕.

异步只管提交任务,

 

 

Day 42 协程. IO 并发

 

 

Day 42 协程. IO 并发

 

 

 

Day 42 协程. IO 并发

 

  Day 42 协程. IO 并发

 

 Day 42 协程. IO 并发

Day 42 协程. IO 并发

 

 Day 42 协程. IO 并发

 

相关文章:

  • 2021-10-07
  • 2021-11-06
  • 2022-12-23
  • 2021-09-06
  • 2022-02-09
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-12-13
  • 2022-12-23
  • 2021-12-04
  • 2022-12-23
  • 2022-12-23
  • 2021-12-30
相关资源
相似解决方案