13

我正在使用 UDP 在 python 中进行一些线程异步网络实验。

我想了解 polling 和 select python 模块,我从未在 C/C++ 中使用过它们。

那些是为了什么?我有点理解选择,但是在观看资源时它会阻塞吗?投票的目的是什么?

4

3 回答 3

16

好吧,一次一个问题。

那些是干什么用的?

这是一个简单的套接字服务器框架:

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 准备好读取/写入/或发生异常时通知您。

它(选择)在观看资源时会阻塞吗?

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

正如选择手册页所说,它将获取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. timeout.tv_sec == 0 和 timeout.tv_usec = 0

    无阻塞,立即返回

  2. 超时 == NULL

    永远阻塞,直到文件描述符准备好。

  3. 超时是正常的

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

投票的目的是什么?

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

这是基于以下简单事实

  1. CPU 比 IO 快得多
  2. 等待IO是浪费时间,因为大部分时间,CPU会处于空闲状态

希望能帮助到你。

于 2015-03-28T08:58:24.307 回答
12

如果你这样做readrecv,你只在等待一个连接。如果你有多个连接,你将不得不创建多个进程或线程,浪费系统资源。

使用selector pollor epoll,您可以仅使用一个线程监控多个连接,并在其中任何一个有可用数据时得到通知,然后您在相应的连接上调用reador 。recv

它可能会无限阻塞、阻塞给定时间或根本不阻塞,具体取决于参数。

于 2011-09-18T08:39:19.047 回答
2

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

因此,如果套接字在等待 timeout 秒后没有尝试连接,那么列表 ready_to_read 将为空——此时,accept() 和 recv() 是否会阻塞并不重要——它们不会因为空而被调用列表....

如果套接字已准备好读取,则 if 将有数据,因此它也不会阻塞。

于 2013-12-13T19:46:12.917 回答