• IO多路复用


    1 IO多路复用:

        检测多个socket是否发生变化(是否链接成功(可读),或者是否获取到信息(可写))

    2.socket服务端默认是在connect,recv,阻塞

    3.如何让socket编程不阻塞?

      使用socket模块的setbloking方法,将阻塞的地方全部变成非阻塞,有助于提高单线程并发效率。但是会抛BlockingIOError异常,捕获即可。

    4.系统检测socket是否发生了变化有三种方法

      1.select:最多监听1024个socket,循环去检测

      2.poll:不限制监听的次数,循环去检测(水平触发)

      3.epoll:不限制监听的系数,回调方法(边缘触发)

    5.提高并发的方案:

      1.多进程:密集型计算

      2.多线程:IO操作

      3.异步非阻塞模块(twisted)基于时间循环的异步非阻塞模块

    ,scrapy框架(单线程完成并发)

    6.什么是异步非阻塞?

      非阻塞:就是不等待socket的setblocking方法

      比如创建socket对某个地址进行connect,获取数据recv时会默认阻塞(链接成功后才会接受到数据),才会执行后边的操作,效率不高,会等待前一个socket执行完了之后才执行后边的socket。

    但是设置setblocking(False),connect,和recv就不会等待,但是会报BlockingIOError错,捕获异常即可!

      异步:起通知的作用!(烧水)

      执行完之后会自动执行回调函数,或者自动执行某些功能。比如做爬虫的时候,执行完之后会自动执行回调函数!

    7.什么是同步阻塞?

      就是等一个socket执行完之后才会执行下一个socket,按照顺序执行。

    单线程解决并发
    def get_data(key):
        client=socket.socket()
        client.connect(('www.baidu.com',80))
        client.sendall(b'GET /s?wd=%alex HTTP/1.0
    host:www.baidu.com
    
    ' )
        chunk_list=[]
        while 1:
            chunk=client.recv(8096)
            if not chunk:
                break
            chunk_list.append(chunk)
        boby=b''.join(chunk_list)
        print(boby.decode('utf8'))
    key_list=['alex','sb','db']
    for item in key_list:
        get_data(item)

     4.单线程的并发:select    setbloking

    import socket
    import select#检测socket是否发生了变化
    
    client1
    =socket.socket() client1.setblocking(False)#将本该阻塞的变成非阻塞的 try: client1.connect(('www.baidu.com',80)) except BlockingIOError as e : pass

    client2
    =socket.socket() client2.setblocking(False) try: client2.connect(('www.sougou.com',80)) except BlockingIOError as e : pass

    client3
    =socket.socket() client3.setblocking(False) try: client3.connect(('www.oldboyedu.com',80)) except BlockingIOError as e : pass

    socket_list
    =[client1,client2,client3]#检测是否服务端给我返回数据了 可读 coon_list=[client1,client2,client3]#检测是否所有的socket和服务器链接成功了 可写 while 1: rlist,wlist,elist=select.select(socket_list,coon_list,[],0.005) # wlist表示已经和服务端连接成功 for sk in wlist:#检查是否已经和服务端连接完成 if sk ==client1: sk.sendall(b'GET /s?wd=alex HTTP/1.0 host:www.baidu.com ') elif sk ==client2: sk.sendall(b'GET /web?query=fdf HTTP/1.0 host:www.sogou.com ') else: sk.sendall(b'GET /s?wd=alex HTTP/1.0 host:www.oldboyedu.com ') coon_list.remove(sk)

      for sk in rlist: chunk_list=[] while 1: try: chunk=sk.recv(8096) if not chunk:#chunk接收到的数据为空的话,就表示接收完毕 break chunk_list.append(chunk) except BlockingIOError as e: break boby=b''.join(chunk_list) print('--------->',boby.decode('utf8')) # print('--------->',boby) sk.close() socket_list.remove(sk) if not socket_list: break

    高级版单线程并发 
    import socket
    import select
    
    class Req(object):
        def __init__(self,sk,func):
            self.sock = sk
            self.func = func
    
        def fileno(self):
            return self.sock.fileno()
    
    
    class Nb(object):
    
        def __init__(self):
            self.conn_list = []
            self.socket_list = []
    
        def add(self,url,func):
            client = socket.socket()
            client.setblocking(False)  # 非阻塞
            try:
                client.connect((url, 80))
            except BlockingIOError as e:
                pass
            obj = Req(client,func)
            self.conn_list.append(obj)
            self.socket_list.append(obj)
    
        def run(self):
    
            while True:
                rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005)
                # wlist中表示已经连接成功的req对象
                for sk in wlist:
                    # 发生变换的req对象
                    sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0
    host:www.baidu.com
    
    ')
                    self.conn_list.remove(sk)
                for sk in rlist:
                    chunk_list = []
                    while True:
                        try:
                            chunk = sk.sock.recv(8096)
                            if not chunk:
                                break
                            chunk_list.append(chunk)
                        except BlockingIOError as e:
                            break
                    body = b''.join(chunk_list)
                    # print(body.decode('utf-8'))
                    sk.func(body)
                    sk.sock.close()
                    self.socket_list.remove(sk)
                if not self.socket_list:
                    break
    
    
    def baidu_repsonse(body):
        print('百度下载结果:',body)
    
    def sogou_repsonse(body):
        print('搜狗下载结果:', body)
    
    def oldboyedu_repsonse(body):
        print('老男孩下载结果:', body)
    
    
    t1 = Nb()
    t1.add('www.baidu.com',baidu_repsonse)
    t1.add('www.sogou.com',sogou_repsonse)
    t1.add('www.oldboyedu.com',oldboyedu_repsonse)
    t1.run()
     

     协程:

      协程是由开发人员创造出来的一个不真实存在的东西。

      协程也叫微线程,是对一个线程进行分片,让线程在代码之间相互切换执行,不再是像原来一样按照顺序执行!

    单个的协程没有什么意义,无法实现并发,甚至性能会降低,它和IO 操作相结合,能大大的提高效率,会执行greenlet的switch方法!

    初识协程;

    pip3 install greenlet#安装greenlet模块
    import
    greenlet def f1(): print(11) gr2.switch() print(22) gr2.switch() def f2(): print(33) gr1.switch() print(44) gr1=greenlet.greenlet(f1) gr2=greenlet.greenlet(f2) gr1.switch()

    协程+IO操作:

    from gevent import monkey
    monkey.patch_all() # 以后代码中遇到IO都会自动执行greenlet的switch进行切换
    import requests
    import gevent
    
    
    def get_page1(url):
        ret = requests.get(url)
        print(url,ret.content)
    
    def get_page2(url):
        ret = requests.get(url)
        print(url,ret.content)
    
    def get_page3(url):
        ret = requests.get(url)
        print(url,ret.content)
    
    gevent.joinall([
        gevent.spawn(get_page1, 'https://www.python.org/'), # 协程1
        gevent.spawn(get_page2, 'https://www.yahoo.com/'),  # 协程2
        gevent.spawn(get_page3, 'https://github.com/'),     # 协程3
    ])

    基于yield实现协程

    def f1():
        print(11)
        yield
        print(22)
        yield
        print(33)
    
    def f2():
        print(55)
        yield
        print(66)
        yield
        print(77)
    
    v1 = f1()
    v2 = f2()
    
    next(v1) # v1.send(None)
    next(v2) # v1.send(None)
    next(v1) # v1.send(None)
    next(v2) # v1.send(None)
    next(v1) # v1.send(None)
    next(v2) # v1.send(None)

     重点来啦!

    进程,线程,协程之间的区别?

      进程是计算机资源分配的最小单元,主要用来数据的隔离。

      线程是计算机工作的最小单元,一个进程可以有多个线程(默认只有一个),一个应用程序有对个进程。

      他们在其他语言中并没有这个概念,在python中,一般IO操作使用多线程,计算密集型计算使用多进程,这主要是由python的GIL锁造成的。

      GIL锁就是在同一时刻同一进程只有一个线程才能被CPU调度,如果想使用计算机多核的优势,那就使用多进程,这就是为什么计算密集型操作使用多进程,而IO操作操作则不占用CPU。

      开发人员让代码更加的强大,就出现了协程,协程本身是不存在的,由程序员自己创造的,协程可以在函数之间相互切换,本身没有太大的意义,但是和IO切换放在一起就特别厉害了,可以让一个线程分片,进而达到一个线程不会停止,一遇到IO就切换到别处,当IO操作完成之后还会切回来。在python中使用协程会遇到一个叫做greenlet的模块实现协程+IO自动切换的模块是gevent.

  • 相关阅读:
    从IRP说起(转)
    IoSkipCurrentIrpStackLocation .(转)
    IO_REMOVE_LOCK使用方法小结(转载加改正)
    TCP释放连接时为什么time_wait状态必须等待2MSL时间
    网络编程之select
    Ubuntu18.04 安装Chrome浏览器
    Ubuntu修改系统时间
    Linux常用命令总结
    struct ifconf和struct ifreq,获取网线插入状态
    一个简单的客户单与服务端程序
  • 原文地址:https://www.cnblogs.com/wqzn/p/9643172.html
Copyright © 2020-2023  润新知