您的位置:时时app平台注册网站 > 编程知识 > day10--协成异步IO缓存【时时app平台注册网站】

day10--协成异步IO缓存【时时app平台注册网站】

2019-10-30 04:20

  缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

python 3.x 学习笔记17(协程以及I/O模式),python3.x

1.协程(微线程)
协程是一种用户态的轻量级线程。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

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

2.greenlet模块
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

例子

from greenlet import greenlet

def fun1():
    print(6)
    gar2.switch() #转换到gar2
    print(58)

def fun2():
    print(54)
    gar1.switch()


gar1 = greenlet(fun1) #启动协程
gar2 = greenlet(fun2)
gar1.switch()

 

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

import gevent

def fun1():
    print('第一次运行fun1')
    gevent.sleep(2)           #切换到fun2的gevent.sleep(1)这一步
    print('第二次运行fun1')
def fun2():
    print('第一次运行fun2')
    gevent.sleep(1)            #sleep时间没到继续切换到fun3的gevent.sleep(2)
    print('第二次运行fun2')
def fun3():
    print('第一次运行fun3')
    gevent.sleep(2)
    print('第二次运行fun3')

gevent.joinall( [
    gevent.spawn(fun1),
    gevent.spawn(fun2),
    gevent.spawn(fun3),
])

结果

第一次运行fun1
第一次运行fun2
第一次运行fun3
第二次运行fun2
第二次运行fun1
第二次运行fun3

 

4.gevent默认检测不了urllib的i/o操作

 

5.要异步操作爬虫,必须加上monkey.patch_all(),意思是把当前程序的所有的io操作单独做上标记

from urllib import request
import gevent,time
from gevent import monkey
monkey.patch_all() #把当前程序的所有的io操作单独做上标记
def f(url):
    print('GET%s'%url)
    resp = request.urlopen(url)
    data = resp.read()
    print('%d 数据接收来自%s.' % (len(data), url))

start_time = time.time()
gevent.joinall([
    gevent.spawn(f, 'https://www.python.org/'),
    gevent.spawn(f, 'https://www.baidu.com/'),
    gevent.spawn(f, 'https://github.com/'),
])
print('总共时间:',time.time()-start_time)

 

6.事件驱动模型
目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:
  1). 有一个事件(消息)队列;
  2. 鼠标按下时,往这个队列中增加一个点击事件(消息);
  3). 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
  4). 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;

7.事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。

8.缓存 I/O 

  缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

缓存 I/O 的缺点
  数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

注释:此缓存 I/O 在linux环境下的I/O
详解:

9.IO模式

阻塞 I/O(blocking IO)
非阻塞 I/O(nonblocking IO)
I/O 多路复用( IO multiplexing)
信号驱动 I/O( signal driven IO)
异步 I/O(asynchronous IO)

3.x 学习笔记17(协程以及I/O模式),python3.x 1.协程(微线程) 协程是一种用户态的轻量级线程。 协程拥有自己的寄存器上下文和栈。协...

    SelectPollEpoll异步IO 

注释:此缓存 I/O 在linux环境下的I/O
详解:

    上面代码中,greenlet模块,greenlet类中,主要实现不同方法之间的切换,让程序能上下文进行切换,switch()转换gr1.switch()

例子

import gevent,time
from urllib.request import urlopen
'''导入urllib模块中的urlopen用来打开网页'''

def func(url):
    print("\033[31m----------------------并发的爬取未网页---------------------------\033[0m")
    resp = urlopen(url)                                      #使用urlopen打开一个网址
    data = resp.read()                                       #读取网页里面的内容
    with open("alex.html",'wb') as f:
        f.write(data)
    print("%s个字节被下载,从网页%s。" %(len(data),url))

# if __name__ == "__mian__":
start_time = time.time()
gevent.joinall([
    gevent.spawn(func,"https://www.cnblogs.com/alex3714/articles/5248247.html"),
    gevent.spawn(func,"https://www.python.org/"),
    gevent.spawn(func,"https://www.jd.com/")
    ])
end_time = time.time()
cost_time = end_time - start_time
print("cost time:%s" %cost_time)

 

    "原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。改变量即可称为简单原子操作。协成是在单线程里面实现的。同一时间只有一个线程。

import gevent

def fun1():
    print('第一次运行fun1')
    gevent.sleep(2)           #切换到fun2的gevent.sleep(1)这一步
    print('第二次运行fun1')
def fun2():
    print('第一次运行fun2')
    gevent.sleep(1)            #sleep时间没到继续切换到fun3的gevent.sleep(2)
    print('第二次运行fun2')
def fun3():
    print('第一次运行fun3')
    gevent.sleep(2)
    print('第二次运行fun3')

gevent.joinall( [
    gevent.spawn(fun1),
    gevent.spawn(fun2),
    gevent.spawn(fun3),
])

 

5.要异步操作爬虫,必须加上monkey.patch_all(),意思是把当前程序的所有的io操作单独做上标记

当应用程序需要在任务间共享可变的数据时,这也是一个不错的选择,因为这里不需要采用同步处理。

缓存 I/O 的缺点
  数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

    Greenlet

7.事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。

    综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式

from greenlet import greenlet

def fun1():
    print(6)
    gar2.switch() #转换到gar2
    print(58)

def fun2():
    print(54)
    gar1.switch()


gar1 = greenlet(fun1) #启动协程
gar2 = greenlet(fun2)
gar1.switch()

    协程的好处:

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

    上面代码执行结果如下:

 

    代码执行结果如下:

6.事件驱动模型
目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:
  1). 有一个事件(消息)队列;
  2. 鼠标按下时,往这个队列中增加一个点击事件(消息);
  3). 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
  4). 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;

'''协成:微线程,主要作用是遇到IO操作就切换,程序本身就串行的,主要是在各个IO直接来回切换,节省时间'''
import gevent,time
from gevent import monkey

monkey.patch_all()                                                     #把当前程序的所有的IO操作给单独的做上标记
def fun():
    print("In the function fun!!!n")
    time.sleep(2)                                                      #IO阻塞,也可以是具体执行一个操作,这里直接等待
    print("\033[31mCome back to the function of fun again!\033[0mn")

def inner():
    '''定义一个函数,包含IO阻塞'''
    print("Running in the function of inner!!!n")
    time.sleep(1)
    print("\033[32mRunning back to the function of inner\033[0mn")

def outer(name,arg):
    '''定义一个函数,也包含IO阻塞'''
    print("周末去搞基把,带上%sn" %name)
    time.sleep(0.5)
    print("不好吧,%s是人妖,不好那口,还是带上%s!!!"  %(name,arg))

start_time = time.time()
gevent.joinall([
    gevent.spawn(fun),
    gevent.spawn(inner),
    gevent.spawn(outer,"alex","wupeiqi"),
])
end_time = time.time()
print("花费时间:%s" %(end_time -start_time))

2.greenlet模块
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

    缺点:

 

    上面代码执行结果如下:

第一次运行fun1
第一次运行fun2
第一次运行fun3
第二次运行fun2
第二次运行fun1
第二次运行fun3

当我们面对如下的环境时,事件驱动模型通常是一个好的选择:

from urllib import request
import gevent,time
from gevent import monkey
monkey.patch_all() #把当前程序的所有的io操作单独做上标记
def f(url):
    print('GET%s'%url)
    resp = request.urlopen(url)
    data = resp.read()
    print('%d 数据接收来自%s.' % (len(data), url))

start_time = time.time()
gevent.joinall([
    gevent.spawn(f, 'https://www.python.org/'),
    gevent.spawn(f, 'https://www.baidu.com/'),
    gevent.spawn(f, 'https://github.com/'),
])
print('总共时间:',time.time()-start_time)

    2.无需原子操作锁定及同步的开销;

1.协程(微线程)
协程是一种用户态的轻量级线程。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

    目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:

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

    二 IO模式

4.gevent默认检测不了urllib的i/o操作

    3.方便切换控制流,简化编程模型;

9.IO模式

   协成(Gevent)

结果

    可见,打上补丁之后,from gevent import monkey,声明monkey.patch_all()就能让程序自动检测哪些是IO操作,不需要自己注明。

8.缓存 I/O 

   上面程序中,使用的是协成,我们可以看出,协成遇到IO操作是阻塞的,协成是在串行情况下实现了多并发或异步的效果,整个程序串行执行的话需要至少花费3.5秒,而使用协成花费:2.0312681198120117,可见如果有IO阻塞,协成能够有效降低程序运行的时间。

阻塞 I/O(blocking IO)
非阻塞 I/O(nonblocking IO)
I/O 多路复用( IO multiplexing)
信号驱动 I/O( signal driven IO)
异步 I/O(asynchronous IO)

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

 

----------------------并发的爬取未网页---------------------------
----------------------并发的爬取未网页---------------------------
----------------------并发的爬取未网页---------------------------
124072个字节被下载,从网页https://www.jd.com/。
91829个字节被下载,从网页https://www.cnblogs.com/alex3714/articles/5248247.html。
48721个字节被下载,从网页https://www.python.org/。
cost time:1.7704188823699951

  番外篇

import socket
client = socket.socket()
ip,port = 'localhost',9999
client.connect((ip,port))
while True:
    mess = input(">>:").encode("utf-8").strip()
    if len(mess) == 0:
        print("发送消息不能为空!!!")
        continue
    client.send(mess)
    data = client.recv(1024)
    print("客户端接收返回数据:%s" 
	  

本文由时时app平台注册网站发布于编程知识,转载请注明出处:day10--协成异步IO缓存【时时app平台注册网站】

关键词: