【问题标题】:I can't understand polling/select in python我无法理解 python 中的轮询/选择
【发布时间】:2011-11-19 12:56:29
【问题描述】:

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

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

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

【问题讨论】:

    标签: python multithreading sockets polling epoll


    【解决方案1】:

    如果您选择readrecv,则您只等待一个连接。如果你有多个连接,你将不得不创建多个进程或线程,浪费系统资源。

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

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

    【讨论】:

    • Yes.... select() 应该“处理”任何与套接字相关的调用,因为所有 select() 所做的只是告诉进程“阻塞,直到这些套接字之一有数据可供读取,或可用于写入的缓冲区空间,或直到经过这么多时间”。您在 select() 返回后对套接字所做的事情并不是 select() 会知道或关心的事情。
    【解决方案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() 是否阻塞并不重要——它们不会被调用对于空列表....

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

    【讨论】:

      【解决方案3】:

      好的,一次一个问题。

      这些是干什么用的?

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

      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 man page所说,它会得到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 会处于空闲状态

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 2013-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-10
        • 1970-01-01
        • 2021-06-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多