• 第四十一天:协程操作


    1.无论是进程还是线程都是由操作系统的时间片时间来进行操控,无法进行人为的控制,并且进行的都是并发程序。从微观上看还是同一时间执行一个程序。

    2.进程是资源分配的最小单位,线程是cpu调度的最小单位。在开启线程的过程中,创建一个线程需要创建一个寄存器和堆栈,这些都是要花费时间的。

    3。协程操作:为了实现并发操作:主要是为了多个任务之间进行切换。

    4.以前学的生产者消费者模型里就含有这种模式的模型:

    def consumer():
        while True:
            x=yield
            print('处理了数据',x)
    def production():
        c=consumer()
        next(c)
        for i in range(10):
            print('生产了数',i)
            c.send(i)
    production()
    View Code

    5.装greenlet模块:打开cmd然后输入C:——enter——输入pip install greenlet 就可以下载了

    6.安装gevent:步骤和上面一样就是输入 pip install  gevent

    7.协程的第一个列子:

    from greenlet import greenlet
    import time
    def eat():
        print('eat start')
        time.sleep(1)
        print('eat end')
    def play():
        print('play start')
        time.sleep(1)
        print('play end')
    g1=greenlet(eat)#只是进行实例化,并没有执行该函数
    g2=greenlet(play)
    View Code

     从结果可以看出没有执行任何指令:

    8.使用switch才能执行函数里的指令(此语句代表的意思是切换)

    from greenlet import greenlet
    import time
    def eat():
        print('eat start')
        time.sleep(1)
        g2.switch()#记住执行的位置,并切换到play去执行
        print('eat end')
    def play():
        print('play start')
        time.sleep(1)
        g1.switch()#记住执行的位置,并切换到play去执行
        print('play end')
    g1=greenlet(eat)#只是进行实例化,并没有执行该函数
    g2=greenlet(play)
    g1.switch()#切换到eat函数去执行
    结果为
    eat start
    play start
    eat end
    View Code

    从结果可以看出切换到eat执行完成之后并由切换到play执行最后的语句,使用的解决方法是:

    9.计算时间执行和协程切换执行花费的时间:

      顺序执行的时间:

    import time
    from greenlet import greenlet
    def func():
        for i in range(10000000):
            i*i
    def func1():
        for i in range(10000000):
            i+i
    start=time.time()
    func()
    func1()
    t1=time.time()-start
    print(t1)
    结果为
    1.0864977836608887
    View Code

      使用协程操作的时间

    import time
    from greenlet import greenlet
    def func():
        for i in range(10000000):
            i*i
            g2.switch()
    def func1():
        for i in range(10000000):
            i+i
            g1.switch()
    start=time.time()
    g1=greenlet(func)
    g2=greenlet(func1)
    g1.switch()
    g2.switch()
    t1=time.time()-start
    print(t1)
    结果为
    5.035648822784424
    View Code

    从两个结果可以看出每次协程之间的转换是要花费时间的。

    10.协程主要用于高IO操作中,当程序在执行过程中如果遇到IO操作,就会进入堵塞状态,程序无法进行下面的流程,如果把这个时间空出来供另一个协程使用,这样就提高了资源的利用率

    11.一般来说进程一般为cpu个数+1 线程为cpu个数的5倍,协程一般最多开500个

    12.虽然协程在切换期间会浪费时间,但是浪费的时间远远小于堵塞的时间。

    13.协程的第个操作指令:gevent

    g1=gevent.spawn(func,参数)创建一个协程对象g1,spawn括号内第一个参数是函数名
    后面的可以是位置参数也可以是关键字参数。
    g1.join()  #等待g1的结束
    g2.join()
    或者使用gevent.joinall([g1,g2])
    g1.value 拿到返回值
    View Code

    14.可以使用from gevent import monkey;monkey.patch_all(),把所有可以发生堵塞的模块进行打包,并且此模块一定要放到所有导入模块的最前面

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    def eat():
        print('eat start')
        time.sleep(1)
        print('eat end')
    def play():
        print('play start')
        time.sleep(1)
        print('play end')
    g1=gevent.spawn(eat)
    g2=gevent.spawn(play)
    g1.join()
    g2.join()
    结果为
    eat start
    play start
    eat end
    play end
    View Code

    15。协程的执行是否依靠join操作

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    def eat():
        print('eat start')
        time.sleep(1)
        print('eat end')
    def play():
        print('play start')
        time.sleep(1)
        print('play end')
    g1=gevent.spawn(eat)
    g2=gevent.spawn(play)
    g1.join()
    View Code

    从结果我们可以看出只有启动一个协程就可以执行所有实例化的协程

    16.协程里面的任务切换是通过greenlet中的switch进行的。

    17进程和线程里的切换操作都是由操作系统控制完成的,而协程里的切换操作是由程序操作的(代码)。

    18只有遇到高要求的IO操作时,我们才使用协程操作,实行并发的效果。

    19.查看协程号的程序:

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    import threading
    def eat():
        print('eat start')
        print(threading.current_thread(),threading.get_ident)
        time.sleep(1)
        print('eat end')
    def play():
        print('play start')
        print(threading.current_thread(),threading.get_ident)
        time.sleep(1)
        print('play end')
    g1=gevent.spawn(eat)
    g2=gevent.spawn(play)
    g1.join()
    结果为
    eat start
    <_DummyThread(DummyThread-1, started daemon 1840173395744)> <function get_ident at 0x000001AC72E580D0>
    play start
    <_DummyThread(DummyThread-2, started daemon 1840177303776)> <function get_ident at 0x000001AC72E580D0>
    eat end
    play end
    View Code

    从结果来看协程是假的线程,但是两个协程都来自同一个协程。

    19.同步和异步的例子(就是使用协程和不使用协程):

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    import threading
    def task():
        time.sleep(1)
        print(1234)
    def same():
        for i in range(10):
            task()
    def different():
        g_list=[]
        for i in range(10):
            g1=gevent.spawn(task)
            g_list.append(g1)
        gevent.joinall(g_list)
    same()
    different()
    View Code

    从结果来看,同步是一个一个执行,异步是一次性输出所有接过执行原理是,当遇到sleep时,程序进入堵塞,协程这个时候就会切换到下面一次程序进行,然后继续堵塞,继续切换,等到哪个程序执行完成后再进行切换,切换过来以后,那写程序sleep时间都到了,所以可以实现并发的效果。

    20.协程主要是能够在一个线程中实现并发效果。能够规避一些任务中的IO操作‘在任务执行的过程当中,检测到IO操作就可以切换到其他任务去执行。

    21.协程的操作主要应用与爬虫和请求IO的等待。

    22.爬虫的小例子:

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    import requests
    def acquire_url(url):
        ret=requests.get(url)
        ret=ret.content.decode('utf-8')
        return len(ret)
    g1=gevent.spawn(acquire_url,'http://www.baidu.com')
    g2=gevent.spawn(acquire_url,'http://www.sougou.com')
    g3=gevent.spawn(acquire_url,'https://cn.bing.com/?mkt=zh-CN&mkt=zh-CN&mkt=zh-CN')
    g4=gevent.spawn(acquire_url,'https://www.sohu.com/')
    g5=gevent.spawn(acquire_url,'https://www.so.com/')
    gevent.joinall([g1,g2,g3,g4,g5])
    print(g1.value)
    print(g2.value)
    print(g3.value)
    print(g4.value)
    print(g5.value)
    View Code

    23使用协程来写socket的程序:

    server端程序:

    from gevent import monkey;monkey.patch_all()
    import socket
    import gevent
    def connection(conn):
        conn.send('你好'.encode('utf-8'))
        msg=conn.recv(1024).decode('utf-8')
        print(msg)
    
    sk=socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    while True:
        conn,addr=sk.accept()
        g1=gevent.spawn(connection,conn)
    View Code

      client端程序:

    import socket
    sk=socket.socket()
    sk.connect(('127.0.0.1',8080))
    while True:
        msg=sk.recv(1024).decode('utf-8')
        print(msg)
        info=input('请输入信息').encode('utf-8')
        sk.send(info)
    View Code

    协程执行的过程就是把每一次获取到的结果放到一片空间中,等遇到堵塞了再去执行下一个协程。

  • 相关阅读:
    [WPF系列] 需要区分的内容
    [WPF系列]基础 Listening to Dependency Property change notifications of a given Element
    [WPF系列]-基础系列 Property Trigger, DataTrigger & EventTrigger
    [WPF系列]-高级部分 Shadowed TextBox
    [WPF系列]-Adorner
    [WPF系列]-使用Binding来同步不同控件的Dependency property
    各类型液晶电视面板解析
    数据库 --> 8种NoSQL数据库对比
    数据库 --> 5种关系型数据库比较
    云计算 --> 三种服务模式IaaS,PaaS,SaaS
  • 原文地址:https://www.cnblogs.com/ab461087603/p/12532024.html
Copyright © 2020-2023  润新知