多线程Python进程运行时,操作系统开始终止进程。

6

这真是太奇怪了!

我有一个用Python编写的多线程客户端应用程序。我使用线程来并发下载和处理页面。虽然在这个应用程序中瓶颈肯定是处理器(而不是带宽),所以使用线程池更有效,但我不会使用cURL多句柄。

我有一台64位i7电脑,配备16GB内存。非常强大。当我听Pandora音乐和浏览Stackoverflow时,我启动了80个线程,结果父进程有时会以“Killed”消息结束。

其他时候,单个页面(在Chrome中作为自己的进程)会崩溃。还有时候整个浏览器会崩溃。

如果您想查看代码,请参考以下要点:

这是主进程:

def start( ):
  while True:
    for url in to_download:
      queue.put( ( url, uri_id ) )

    to_download = [ ]

    if queue.qsize( ) < BATCH_SIZE:
      to_download = get_more_urls( BATCH_SIZE )

    if threading.activeCount( ) < NUM_THREADS:
      for thread in threads:
        if not thread.isAlive( ):
          print "Respawning..."
          thread.join( )
          threads.remove( thread )
          t = ClientThread( queue )
          t.start( )
          threads.append( t )

    time.sleep( 0.5 )

以下是ClientThread的要点:

class ClientThread( threading.Thread ):

  def __init__( self, queue ):
    threading.Thread.__init__( self )
    self.queue = queue

  def run( self ):
    while True:
      try:
        self.url, self.url_id = self.queue.get( )
      except:
        raise SystemExit

      html = StringIO.StringIO( )
      curl = pycurl.Curl( )
      curl.setopt( pycurl.URL, self.url )
      curl.setopt( pycurl.NOSIGNAL, True )
      curl.setopt( pycurl.WRITEFUNCTION, html.write )
      curl.close( )

      try:
        curl.perform( )
      except pycurl.error, error:
        errno, errstr = error
        print errstr

      curl.close( )

EDIT: 噢,对了...忘记提问了...应该很明显:为什么我的进程会被杀掉?是在操作系统层面上发生的吗?内核级别?这是由于我可以拥有的开放TCP连接数限制吗?是一次只能运行几个线程的限制吗?cat /proc/sys/kernel/threads-max的输出是257841。所以...我不认为是那个问题....
我想我已经找到了答案...好的...我的驱动器上根本没有交换空间。现在有办法创建一些交换空间吗?我正在运行Fedora 16。曾经有过交换空间...然后我启用了所有的RAM,它就神奇地消失了。尾随/ var / log / messages,我发现了这个错误:
Mar 26 19:54:03 gazelle kernel: [700140.851877] [15961]   500 15961    12455     7292   1       0             0 postgres
Mar 26 19:54:03 gazelle kernel: [700140.851880] Out of memory: Kill process 15258 (chrome) score 5 or sacrifice child
Mar 26 19:54:03 gazelle kernel: [700140.851883] Killed process 15258 (chrome) total-vm:214744kB, anon-rss:70660kB, file-rss:18956kB
Mar 26 19:54:05 gazelle dbus: [system] Activating service name='org.fedoraproject.Setroubleshootd' (using servicehelper)

1
检查 dmesg(8) 输出以查看内核是否记录了任何信息。 - sarnold
dmesg(8)中的最后几项内容涉及我的wifi适配器与路由器的关联。......而这是几个小时前的事了。 - KeatsKelleher
2个回答

8
您已触发内核的 Out Of Memory(OOM)处理程序;它以一种复杂的方式选择要终止的进程,试图尽可能少地终止进程,以产生最大的影响。 根据内核使用的标准,Chrome 显然是最容易被终止的进程。您可以在 proc(5) 手册的 /proc/[pid]/oom_score 文件中查看标准的摘要。
   /proc/[pid]/oom_score (since Linux 2.6.11)
          This file displays the current score that the kernel
          gives to this process for the purpose of selecting a
          process for the OOM-killer.  A higher score means that
          the process is more likely to be selected by the OOM-
          killer.  The basis for this score is the amount of
          memory used by the process, with increases (+) or
          decreases (-) for factors including:

          * whether the process creates a lot of children using
            fork(2) (+);

          * whether the process has been running a long time, or
            has used a lot of CPU time (-);

          * whether the process has a low nice value (i.e., > 0)
            (+);

          * whether the process is privileged (-); and

          * whether the process is making direct hardware access
            (-).

          The oom_score also reflects the bit-shift adjustment
          specified by the oom_adj setting for the process.

如果你想要Python程序不被杀死,你可以调整oom_score文件。但更好的方法可能是为系统添加更多交换空间,以尽量推迟OOM-killer的触发时间。当然,拥有更多的交换空间并不一定意味着你的系统永远不会耗尽内存,而且如果存在大量的交换流量,你可能不喜欢它的处理方式,但至少可以帮助你解决紧急的内存问题。
如果你已经分配了所有可用的交换分区空间,你可以添加交换文件。由于它们通过文件系统进行操作,所以与交换分区相比,交换文件有更多的开销,但是在分区后添加它们可以使其成为一个简单的短期解决方案。你可以使用dd(1)命令来分配文件(不要使用seek创建稀疏文件),然后使用mkswap(8)对文件进行格式化以供交换使用,最后使用swapon(8)打开该特定文件。(我认为你甚至可以将交换文件添加到fstab(5)中,以便在下次重新启动时自动可用,但我从未尝试过,也不知道语法。)

请注意,Chrome渲染器进程通过将硬编码的“300”写入“/ proc / self / oom_score_adj”自愿进行杀死,而不考虑您实际的RAM。在现代系统中,Chrome进程将被超负荷地使用许多GB的RAM,同时决定要杀死以释放RAM的进程。如果您希望在Chrome之前杀死您的进程,则必须使用类似“500”的值。有关详细信息,请参见https://bugs.chromium.org/p/chromium/issues/detail?id=333617。 - Mikko Rantalainen

0

你正在进行编程

raise SystemExit

这实际上是退出 Python 解释器,而不是您正在运行的线程。


没关系,这并不能解释为什么一个完全独立的进程会死掉。请参见上面的编辑。 - KeatsKelleher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接