• 网络编程之协程


    什么是用户态和内核态:

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

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

      内核态和用户态的详情: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时)会阻塞掉整个程序

  • 相关阅读:
    UISegmentControl
    UISegmentControl,UISwich,UIPageControl
    UIPageControl,UISlider
    UISlider
    触摸事件,响应者链和手势
    NIO Socket
    处理多线程问题的工具类
    Java多线程疑难点
    软中断和硬中断的联系与区别
    进程—内存描述符(mm_struct)
  • 原文地址:https://www.cnblogs.com/fangjie0410/p/7684365.html
Copyright © 2020-2023  润新知