epoll 模板

select 的问题:

1.当进程被唤醒不清楚到底哪个socket有数据,只能遍历一遍

2.每一次select的执行,都需要将这进程,再加入到等待队列中

​ 为了防止重复添加等待队列,当某一次操作完成时,也必须从等待队列中删除进程

所以select最大限制被设置为了1024 ,如此看来select连多线程都比不上

于是推出了poll 和 epoll

poll只是简单对select进行了优化,但是还不够完美 ,epoll才是最后的解决方案

注意:epoll仅能在linux中使用

案例:

import socket
import select

s = socket.socket()
s.bind(("127.0.0.1",1689))
s.listen()

# 创建一个epoll对象
epoll = select.epoll()

# 注册读就绪事件 (有数据可以读取了)
# s.fileno()用于获取文件描述符
epoll.register(s.fileno(),select.EPOLLIN)


# 存储文件描述符与socket的对应关系
fd_sockets = {s.fileno():s}


while True:
    # 该函数是阻塞会直到你关注的事件发生
    # 返回值为文件描述符与发生的事件类型  是一个列表 列表中是元组  第一个是描述符 第二个是事件
    for fd,event in epoll.poll():
        print("有socket 搞事情了!")
        sock = fd_sockets[fd] # 取出对应的socket对象

        # 判断socket是服务器还是客户端
        if sock == s:
            # 执行对应的接收或发送
            client,addr = sock.accept()
            # 注册客户端的事件
            epoll.register(client.fileno(),select.EPOLLIN)
            # 将对应关系存储到字典中
            fd_sockets[client.fileno()] = client
            print("来了一个客户端....")
            
        elif event == select.EPOLLIN: #客户端的处理
            data = sock.recv(1024)
            if not data:
                epoll.unregister(fd) # 注销事件
                fd_sockets.pop(fd) # 从字典中删除
                sock.close()  # 关闭socket
                continue

            print("%s 发来问候:%s" % (sock,data.decode("utf-8")))

            #将事件转换为可写
            epoll.modify(fd,select.EPOLLOUT)
        else:
            sock.send("我是服务器  你丫是谁?".encode("utf-8"))
            # 将事件转换为可读
            epoll.modify(fd, select.EPOLLIN)

epoll 如何解决select的两个问题

1.epoll 把对于等待队列的操作 与阻塞进程分开了

2.epoll 自己维护了一个等待队列 避免了遍历所有socket

并发:

多进程 开销大

多线程 开销小于进程 但是不能无限开

协程 避免线程数量达到上线的问题 本质上属于非阻塞IO模型

IO模型 多路复用 是最好的解决方案

面试官如果问到高并发,从进程开始介绍

原文地址:https://www.cnblogs.com/bladecheng/p/11164536.html