【问题标题】:Handle out of range in select在选择中处理超出范围
【发布时间】:2013-02-13 12:35:14
【问题描述】:

许多 (>1000) 工人(流程)做了一些工作并希望将他们的工作结果保存在数据库中。工作结果是 JSON 对象。工人每秒产生 1-5 个 JSON 对象。数据库保护程序是独立的进程。将 JSON 对象从 worker 传输到 saver 的单向连接是 multiprocessing.Pipe。管道数等于工人数。

在保护程序中定期调用:

def recv_data(self):
    data = []
    for pipe in self.data_pipe_pool:
        if pipe.poll():
            data.append(pipe.recv())
    return data

self.data_pipe_pool - 来自工人的管道列表。

如果我运行约 100 名工人,一切正常。如果我运行 >1000 个工人,我会得到异常:

2013-02-13T15:17:40.731429
Traceback (most recent call last):
  File "saver.py", line 44, in run
    profile = self.poll_data()
  File "saver.py", line 116, in poll_data
    ret = self.recv_data()
  File "saver_unit.py", line 127, in recv_data
    if pipe.poll():
IOError: handle out of range in select()

我知道这是由于select() 调用,并且:

FD_SETSIZE 在 GNU/Linux 系统中通常定义为 1024

但是哪里叫select?如果在pipe.poll() 中,为什么我超过了 FD_SETSIZE 限制,我分别为 1 个管道调用 pipe.poll()?我在哪里可以通过此调用select 观看 python 语言源?

什么解决方法不超过FD_SETSIZE限制或不使用select

【问题讨论】:

    标签: python linux ipc pipe


    【解决方案1】:

    如果你检查select manual page,你会看到:

    使用 fd 为负数或等于或大于 FD_SETSIZE 的值执行 FD_CLR() 或 FD_SET() 将导致未定义的行为。

    这意味着如果 select 在您的 poll 调用中在幕后使用(这似乎很可能),并且您的文件描述符大于 FD_SETSIZE(如果您有超过 1000 个管道,则很可能)然后结果可以是任何东西。

    【讨论】:

    • 感谢您的回答。我怎样才能避免影子电话select?可能存在低级别的 epoll api 管道(或对象上方)?
    【解决方案2】:

    您有没有想过使用像 beanstalkd 这样的东西,因为听起来您有一个从工作人员到服务器的管道,而通常您有 1000 个工作人员,然后有 10 个服务器从结果队列中读取以存储它们在数据库中。

    这样做的好处是,您的工作人员可能会花费更多的时间来完成他们的工作,而不是与服务器交谈,但您已经为该服务器打开了一条管道。

    您可以有一个“工作队列”供您的 1000 名工作人员使用,也可以有一个“结果队列”供他们存储结果。此后,您可以让许多服务器从“结果队列”中存储在数据库中。

    “至少你不会用完文件句柄”这句话很长。

    【讨论】:

      【解决方案3】:

      我使用 epoll 解决了这个问题。解决方法很简单:

      def set_data_pipe_poll(self, data_pipe_poll):
          self.epoll = select.epoll()
          for p in data_pipe_poll:
              self.epoll.register(p, select.EPOLLIN)
          self.data_pipe_poll = data_pipe_poll
      
      def recv_data(self):
          data = []
          events = self.epoll.poll(timeout = 0)
          for fileno, _ in events:
              p = filter(lambda x: x.fileno() == fileno, self.data_pipe_poll)[0]
              data.append(p.recv())
          return data
      

      当我打电话给epoll.poll() 时,我不会打电话给select

      【讨论】:

        【解决方案4】:

        我有类似的问题。我为每个工作人员使用了 multiprocessing.Queue,当工作人员数量约为 600 时,队列方法以IOError: handle out of range in select() 失败。

        我在导致问题的官方错误跟踪器中找到了issue。它在 2.7.4 中修复(我有 2.7.3)。更新python包解决了这个问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-10
          • 2013-11-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多