我无法理解 python 中的轮询/选择



我正在用python做一些线程异步网络实验,使用UDP。

我想了解轮询和选择python模块,我从来没有在C/c++中使用过它们。

这些是干什么用的?我有点理解select,但它在观看资源时会阻塞吗?轮询的目的是什么?

好,一个问题一个问题。

这些是干什么用的?

下面是一个简单的socket服务器框架:
s_sock = socket.socket()
s_sock.bind()
s_sock.listen()
while True:
    c_sock, c_addr = s_sock.accept()
    process_client_sock(c_sock, c_addr)

服务器将循环并接受来自客户端的连接,然后调用其进程函数与客户端套接字通信。这里有一个问题: process_client_sock可能需要很长时间,甚至包含一个循环(通常是这种情况)

def process_client_sock(c_sock, c_addr):
    while True:
        receive_or_send_data(c_sock)

在这种情况下,服务器无法接受任何连接。

一个简单的解决方案是使用多进程或多线程,只创建一个新的线程来处理请求,而主循环继续监听新的连接。

s_sock = socket.socket()
s_sock.bind()
s_sock.listen()
while True:
    c_sock, c_addr = s_sock.accept()
    thread = Thread(target=process_client_sock, args=(c_sock, c_addr))
    thread.start()

这当然有效,但考虑到性能还不够好。因为新的进程/线程占用额外的CPU和内存,不空闲的服务器可能会得到成千上万的连接。

所以selectpoll系统调用试图解决这个问题。你给select一组文件描述符,并告诉它通知你,如果任何fd准备读/写/或异常发生。

它(选择)块,而观看资源?

是或否取决于你传递给它的参数。

如select手册页所说,它将获得struct timeval参数

int select(int nfds, fd_set *readfds, fd_set *writefds,
       fd_set *exceptfds, struct timeval *timeout);
struct timeval {
long    tv_sec;         /* seconds */
long    tv_usec;        /* microseconds */
};

有三种情况:

  1. 超时。Tv_sec == 0和timeout。Tv_usec = 0

    无阻塞,立即返回

  2. timeout == NULL

  3. 超时正常

    等待一段时间,如果仍然没有可用的文件描述符,则超时并返回

轮询的目的是什么?

简而言之:轮询在等待IO时为其他工作释放CPU。

这是基于一个简单的事实

  1. CPU比IO快得多
  2. 等待IO是浪费时间,因为在大多数时间,CPU将是空闲的

希望能有所帮助。

如果执行readrecv,则只等待一个连接。如果你有多个连接,你将不得不创建多个进程或线程,这是对系统资源的浪费。

使用selectpollepoll,您可以仅用一个线程监视多个连接,并在其中任何一个有可用数据时得到通知,然后在相应的连接上调用readrecv

它可以无限阻塞,在给定时间阻塞,或者根本不阻塞,这取决于参数。

select()接受3个套接字列表来检查三个条件(读、写、错误),然后返回(通常较短,通常为空)实际准备为这些条件处理的套接字列表。

s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.bind((Local_IP, Port1))
s1.listen(5)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.bind((Local_IP, Port2))
s2.listen(5)
sockets_that_might_be_ready_to_read = [s1,s2]
sockets_that_might_be_ready_to_write_to = [s1,s2]
sockets_that_might_have_errors = [s1,s2]

([ready_to_read], [ready_to_write], [has_errors])  = 
       select.select([sockets_that_might_be_ready_to_read],
                     [sockets_that_might_be_ready_to_write_to], 
                     [sockets_that_might_have_errors],            timeout)

for sock in ready_to_read:
    c,a = sock.accept()
    data = sock.recv(128)
    ...
for sock in ready_to_write:
    #process writes
    ...
for sock in has_errors:
    #process errors

因此,如果套接字在等待超时秒后没有尝试连接,则列表ready_to_read将为空-此时accept()和recv()是否阻塞无关紧要-它们不会被调用为空列表....

如果一个套接字已经准备好读取,那么它将有数据,所以它也不会阻塞它。

相关内容

  • 没有找到相关文章

最新更新