广告
返回顶部
首页 > 资讯 > 后端开发 > Python >IO多路复用丶基于IO多路复用+sock
  • 634
分享到

IO多路复用丶基于IO多路复用+sock

多路复用IO 2023-01-30 22:01:55 634人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

  io多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作   IO多路复用作用:     检测多个Socket是否已经发生变化(是否已经连接成功/是否已经获取数据

  io多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作

  IO多路复用作用:

    检测多个Socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写)

    操作系统检测socket是否发生变化有三种模式:

      select:最多1024个socket,循环去检测

      poll:不限制监听socket个数,循环去检测(水平触发)

      epoll:不限制监听socket个数:回调方式(边缘触发).

    python模块:

      select.select

      select.epoll

  Python中有一个select模块,其中提供了:select丶poll丶epoll三个方法,分别调用系统的select,poll,epoll从而实现IO多路复用

  注意: 网络操作丶文件操作丶终端操作等均属于IO操作,对于windows只支持socket操作,其他系统支持其他IO操作,但是无法检测普通文件操作,自动上次读取是否已经变化

  当我们需要向百度发送请求搜索三个关键字,我们改怎么办呢?

  单线程解决并发:

方式一:

key_list = ['alex','db','sb']
for item in key_list:
    ret = requests.get('https://www.baidu.com/s?wd=%s' %item)
View Code

方式二:

def get_data(key):
    # 方式二
    client = socket.socket()

    # 百度创建连接: 阻塞
    client.connect(('www.baidu.com',80))

    # 问百度我要什么?
    client.sendall(b'GET /s?wd=alex Http/1.0\r\nhost:www.baidu.com\r\n\r\n')

    # 我等着接收百度给我的回复
    chunk_list = []
    while True:
        chunk = client.recv(8096)
        if not chunk:
            break
        chunk_list.append(chunk)

    body = b''.join(chunk_list)
    print(body.decode('utf-8'))

key_list = ['alex','db','sb']
for item in key_list:
    get_data(item)
View Code

  多线程解决并发:

import threading

key_list = ['alex','db','sb']
for item in key_list:
    t = threading.Thread(target=get_data,args=(item,))
    t.start()
View Code

  前面这几个程序在给发送连接请求时,必定会阻塞住,在哪儿等待百度给它回消息.我们可以把阻塞的地方变成非阻塞,这样可以一直给百度发送请求了,不要在哪儿傻傻的等待百度给回复了.

单线程的并发:

import socket
import select



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.soGou.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]
conn_list = [client1,client2,client3]

while True:
    rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)
    # wlist中表示已经连接成功的socket对象
    for sk in wlist:
        if sk == client1:
            sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
        elif sk==client2:
            sk.sendall(b'GET /WEB?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n')
        else:
            sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n')
        conn_list.remove(sk)
    for sk in rlist:
        chunk_list = []
        while True:
            try:
                chunk = sk.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'))
        print('------------>',body)
        sk.close()
        socket_list.remove(sk)
    if not socket_list:
        break
View Code

单线程的并发高级版:

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\r\nhost:www.baidu.com\r\n\r\n')
                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()
View Code

  什么是异步非阻塞?

    非阻塞,不等待

      比如创建socket对某个地址进行connect丶获取接收数据recv时默认都会等待(连接成功或接收到数据),才执行后续操作,如果设置setblocking(False),以上两个过程就不再等待,但是会报BlockingIOError的错误,只要捕获即可

    异步,通知,执行完成之后自动执行回调函数或自动执行某些操作(通知).

      比如做爬虫中向某个地址baidu.com发送请求,当请求执行完成之后自执行回调函

  协程也可以称为"微线程",就是开发者控制线程执行流程,控制先执行某段代码然后再切换到另外函数执行代码,来回切换

  需要强调的是:

    1.Python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到IO或执行时间过长就会被迫交出CPU权限,切换其他线程运行)

    2.单线程内开启进程,一旦遇到IO,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(非IO操作的切换与效率无关)

  对比操作系统控制线程的切换,用户在单线程内控制协程的切换

  优点如下:

    1.协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级

    2.单线程内就可以实现并发的效果,最大限度地利用CPU

  缺点如下:

    1.协程的本质是单线程下,无法利用多核,可以使一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程

    2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

  总结:

    1.必须在只有一个单线程里实现并发(协程本身无法实现并发)

    2.修改共享数据不需加

    3.用户程序里自己保存多个控制流的上下文栈

    4.附加一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield,greenlet都无法实现,就用到了gevent模块(select机制))

  Greenlet模块

  安装: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
gr1 = greenlet.greenlet(f1)
# 协程 gr2
gr2 = greenlet.greenlet(f2)

gr1.switch()
View Code 

  Gevent模块:

  安装:pip3 install gevent

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

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
])
View Code

 

  

 

--结束END--

本文标题: IO多路复用丶基于IO多路复用+sock

本文链接: https://www.lsjlt.com/news/179586.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • IO多路复用丶基于IO多路复用+sock
      IO多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作   IO多路复用作用:     检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据...
    99+
    2023-01-30
    多路 复用 IO
  • Redis的IO多路复用
    一、linux的IO复用函数同一个线程内,多个描述符的IO操作,能够并发交替地顺序执行。epoll只提供三个函数:int epoll_create(int size); #创建epoll句柄int epol...
    99+
    2022-10-18
  • python之IO多路复用
      同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?  不同的人在不同的上下文下给出的答案是不同的。所以先限定一下本文的上下文。  本文讨论的背景是Linux环境下的network IO。  在进行解释之前,首先要说明几...
    99+
    2023-01-31
    多路 复用 python
  • python IO多路复用之select
        说起IO操作我们最先想到的就是读写文件。其实python中对有三种IO操作,打开文件,使用socket进行网络连接和系统的标准输入输出sys.stdin和sys.stdout。我们先来看一段socket服务端的代码:import s...
    99+
    2023-01-31
    多路 复用 python
  • python IO多路复用之epoll详解
    什么是epoll epoll是什么?在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。当然...
    99+
    2022-11-11
  • IO多路复用原理(select、poll and epoll)
    IO多路复用首先要理解什么是多路?什么是复用? 多路:核心需求是要用尽可能少的线程来处理尽可能多的连接,这里的多路是指需要处理的众多连接。 复用:核心需求是要求使用尽可能少的线程,尽可能减少系统开销去处理尽可能多的连接,那么这个复用是指利用...
    99+
    2023-10-01
    服务器 linux
  • Linux IO多路复用之epoll网络编程
    前言 本章节是用基本的linux基本函数加上epoll调用编写一个完整的服务器和客户端例子,可在Linux上运行,客户端和服务端的功能如下: 客户端从标准输入读入一行,发送到服务端 服务端从网络读取一行,然后输...
    99+
    2022-06-04
    linux epoll linux io多路复用 linux io多路复用之epoll网络编程
  • 如何从底层聊下IO多路复用模型
    本篇内容主要讲解“如何从底层聊下IO多路复用模型”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何从底层聊下IO多路复用模型”吧!前言当我们去面试的时候,问到了...
    99+
    2022-10-19
  • PHP+Socket系列之IO多路复用及实现web服务器
    本篇文章给大家带来了关于php+socket的相关知识,其中主要介绍了IO多路复用,以及php+socket如何实现web服务器?感兴趣的朋友下面一起来看一下,希望对大家有帮助。php原生socket之IO多路复用以及实现web服务器多路复...
    99+
    2023-05-14
    php socket
  • 详解IO多路复用机制——select、poll、epoll的原理和区别
    🌟 前言 🐶 大家好,我是周周,目前就职于国内短视频小厂BUG攻城狮一枚。 🤺 如果文章对你有帮助,记得关注、点赞、收藏,一键三连哦,你的支持将成为我最...
    99+
    2023-10-11
    linux 运维 服务器
  • python3--IO模型,阻塞,非阻塞,多路复用,异步,selectors模块
    协程回顾协程 实际上是一个线程执行了多个任务,遇到IO就切换示例:import time import gevent def func():     print('...
    99+
    2023-01-30
    多路 复用 模块
  • BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程
    文章目录 前言基本概念BIO过程NIO过程IO多路复用过程Java NIO编程Java NIO 核心概念Java NIO 示例 总结 前言 上文介绍了网络编程的基础知识,并基于 Jav...
    99+
    2023-08-30
    nio java 网络
  • BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程
    文章目录 前言基本概念BIO过程NIO过程IO多路复用过程Java NIO编程Java NIO 核心概念Java NIO 示例 总结 前言 上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道...
    99+
    2023-08-16
    nio java 网络
  • 多路复用controlfile文件
    --在数据库开启的状态下做SQL>alter system set control_files='/u01/app/oracle/oradata/PROD4/PROD4/control01.ctl',...
    99+
    2022-10-18
  • PHP+Socket中IO多路复用及实现web服务器的方法是什么
    本篇内容介绍了“PHP+Socket中IO多路复用及实现web服务器的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!php原生so...
    99+
    2023-07-05
  • Python多路复用selector模块的基本使用
    目录1. IO多路复用1.1. epoll,poll, select的比较2. selector模块的基本使用1. IO多路复用 O多路复用技术是使用一个可以同时监视多个IO阻塞的中...
    99+
    2022-11-12
  • 控制文件多路复用
    控制文件是oracle数据库中最重要的文件之一。它记录了数据库的名称及其他关键配置,也记录了当前数据库中所有的数据文件和日志文件的位置及状态等重要信息,是数据库启动过程中必须查找并且使用的关键文件。默认情况...
    99+
    2022-10-18
  • Python之I/O多路复用
    回顾Socket一、Socket起源:socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些so...
    99+
    2023-01-31
    多路 复用 Python
  • flume多路复用怎么写
    这篇文章主要介绍“flume多路复用怎么写”,在日常操作中,相信很多人在flume多路复用怎么写问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”flume多路复用怎么写”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-03
  • golang tcp多路复用怎么实现
    在Go语言中,可以使用net包中的Listen函数来创建一个TCP服务器,并使用Accept方法来接受客户端的连接请求。对于多路复用...
    99+
    2023-10-21
    golang
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作