zoukankan      html  css  js  c++  java
  • 网络编程之协程

    什么是用户态和内核态:

     内核态:当一个任务(进程)执行系统调用而陷入了内核代码中执行,这就叫做内核态。

     用户态:当用户在执行自己的代码时,这就叫在用户态

      内核态和用户态的详情:http://www.cnblogs.com/viviwind/archive/2012/09/22/2698450.html

    psw程序状态寄存器:程序状态字(Program Status Word, PSW)又称状态寄存器,主要用于反映处理器的状态及某些计算结果以及控制指令的执行。

      psw程序状态寄存器详情:http://blog.csdn.net/liuqinglong_along/article/details/51645477

    操作系统应用级的识别:每一个cpu都有自己的指令集。操作系统控制着所有的硬件,也就是说操作系统知道CPU所有的指令集。而应用程序只能靠着操作系统来控制着硬件。也就是说CPU的一些指令集是与应用程序有关的。

    一 协程()Coroutine

     1 什么叫协程:单线程内实现并发,又称为微线程或者钎程。说白了就是协程是一种用户态的轻量级线程,协程就是用户程序自己控制调度的。

     怎么在单线程下实现并发:用户从应用程序级别单线程下的切换,注意的是遇到了IO才切。(切换时保存好当时执行的状态)

     协程就是线程内部调度的;而线程和进程都是由操作系统来调度的。

     2 协程的优缺点

      协程的优点:①协程的切换开销更小,属于程序级别的切换,因此操作系统根本就感知不到,因而更加轻量级。

            ②单线程内实现了并发,从而最大限度的利用的CPU。

      协程的缺点:①协程的本质就是在单线程内,因此无法使用多核。(只能创建多个进程,进程内在创建多个线程,线程内在创建协程)

            ②协程只在单线程下,因此只要是所有协程阻塞,这个线程就会被阻塞。

        3 协成特点

      协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器  上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状  态,换种说法:进入上一次离开时所处逻辑流的位置。

    二 协程的创建

     创建方式一:生成器函数创建

      为什么迭代器还要有iter方法:主要用于for循环,因为for循环里面自带iter和next方法,所以在一个迭代器使用for循环的时候还是需要iter 一下的。

    def producer():
        g=consumer()
        next(g)
        for i in range(100):
            print('alex_',i)
            g.send(i)
    
    def consumer():
        while True:
            n = yield
            print('egon_',n)
    
    producer()
    

     创建方式二:greenlet模块创建

      switch:提交任务

    from greenlet import greenlet
    def ect(name):
        print('%s ect 1'%name)
        g2.switch('egon')
        print('%s ect 2'%name)
        g2.switch('egon')
    
    def paly(name):
        print('%s paly 1'%name)
        g1.switch('alex')
        print('%s paly 2'%name)
    
    g1=greenlet(ect)
    g2=greenlet(paly)
    g1.switch('alex')
    

     注意方式一和方式二切换只是实现了单纯的切换。只能一个代码执行完毕后才切换,无法检测到IO。反而还浪费了时间,不建议使用。

     创建方式三:gevent模块的创建

     gevent模块的作用:①检测IO;②自动的切换,切换时还能保存当时执行的状态。

     gevent模块的常用方法

      gevent.skeep:睡眠几秒,用这个可以创建IO。

      spawn:提交任务。括号里面加上一个函数,如果有参数可以传入参数。   

      joinall:等待执行。(等待所有的任务提交完毕过后才执行)

    gevent方式1:

    import gevent
    import time
    def ect(name):
        print('%s ect 1'%name)
        gevent.sleep(2)
        print('%s ect 2'%name)
    
    def paly(name):
        print('%s paly 1' % name)
        gevent.sleep(1)
        print('%s paly 2' % name)
    
    start=time.time()
    s1=gevent.spawn(ect,'alex')
    s2=gevent.spawn(paly,'egon')
    
    gevent.joinall([s1,s2])
    print(time.time()-start)
    

      joinall相当于是多个join的一样,也就是多个join的简化版。括号里面需要传入对象,而对象的格式就是一个字典的格式。  

    from  gevent import monkey;monkey。patch_all():加上补丁。

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    def ect(name):
        print('%s ect 1'%name)
        time.sleep(2)
        print('%s ect 2' % name)
    
    def paly(name):
        print('%s paly 1'%name)
        time.sleep(1)
        print('%s paly 2' % name)
    
    start=time.time()
    s1=gevent.spawn(ect,'alex')
    s2=gevent.spawn(paly,'egon')
    gevent.joinall([s1,s2])
    print(time.time()-start)
    

      补丁的作用:因为gevent不能自动的识别除了gevent以外的IO,因此加上一个补丁,这样就可以识别替他的情况产生的IO了。

    基于协程实现单线程下的并发效果:

    服务端:

    from gevent import monkey;monkey.patch_all()
    import gevent
    from multiprocessing import Process
    from socket import *
    
    def server(ip,port):
        s = socket(AF_INET, SOCK_STREAM)
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        s.bind((ip,port))
        s.listen(5)
        while True:
            conn,addr=s.accept()
            print('%s:%s' % (addr[0], addr[1]))
            g1=gevent.spawn(talk,conn,addr)
    
    def talk(conn,addr):
        while True:
            try:
                data=conn.recv(1024)
                print('%s:%s [%s]' %(addr[0],addr[1],data))
                if not data:break
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
    if __name__ == '__main__':
        server('127.0.0.1',8092)
    

    客户端:

    from threading import Thread
    from socket import *
    
    def client():
        c=socket(AF_INET,SOCK_STREAM)
        c.connect(('127.0.0.1',8092))
    
        while True:
            c.send('hello'.encode('utf-8'))
            data=c.recv(1024)
            print(data.decode('utf-8'))
    
    if __name__ == '__main__':
        for i in range(500):
            t=Thread(target=client)
            t.start()
    

     基于协程实现网络爬虫:

    from gevent import monkey;monkey.patch_all()
    import gevent
    import requests
    from threading import current_thread
    def get(url):
        print('%s get %s' %(current_thread().getName(),url))
        response=requests.get(url)
        if response.status_code == 200:
            return {'url':len(response.text)}
            # print({'url':len(response.text)})
    
    
    g1=gevent.spawn(get,'http://www.baidu.com')
    g2=gevent.spawn(get,'http://www.python.org')
    g3=gevent.spawn(get,'http://www.jd.com')
    
    g1.join()
    g2.join()
    g3.join()
    
    print(g1.value)
    print(g2.value)
    print(g3.value)
    

     协程创建生产者和消费者模型好处:1 减低了生产者和消费者直接的数据差;2 解耦和。

     yield与协成,如图所示:

        

      greenlet实现协成

        

        greenlet机制的主要思想是:生成器函数或者协程函数中的yield语句挂起函数的执行,直到稍后使用next()或send()操作进行恢复为止。  可以使用一个调度器循环在一组生成器函数之间协作多个任务。greentlet是python中实现我们所谓的"Coroutine(协程)"的一个基础库.

     gevent实现协成

        

        注释:同比之前上面两个实现协成的方式而言,当gevent实现协成遇到IO操作时会切换到下一个任务中,大大的节省了cpu执行程序的  时间

        注释:

        协程的好处:无需线程上下文切换的开销,无需原子操作锁定及同步的开销;方便切换控制流,简化编程模型;高并发+高扩展性+低成  本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
        缺点:无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU    上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

  • 相关阅读:
    hi.baidu.com 百度流量统计
    Autofac is designed to track and dispose of resources for you.
    IIS Manager could not load type for module provider 'SharedConfig' that is declared in administration.config
    How to create and manage configuration backups in Internet Information Services 7.0
    定制swagger的UI
    NSwag在asp.net web api中的使用,基于Global.asax
    NSwag Tutorial: Integrate the NSwag toolchain into your ASP.NET Web API project
    JS变量对象详解
    JS执行上下文(执行环境)详细图解
    JS内存空间详细图解
  • 原文地址:https://www.cnblogs.com/fangjie0410/p/7684365.html
Copyright © 2011-2022 走看看