18

我正在处理的当前 Python 应用程序需要使用 1000 多个线程(Pythons 线程模块)。并不是说任何单个线程都以最大 cpu 周期工作,这只是我正在创建的 Web 服务器负载测试应用程序。IE 模拟 200 个 Firefox 客户端,它们都渴望进入 Web 服务器并下载小型 Web 组件,基本上模拟了在几秒而不是微秒内操作的人类。

因此,我正在阅读各种主题,例如“python 在 Linux/windows 上支持多少线程等,我看到了很多不同的答案。一位用户说这都是关于内存的,而 Linux 内核默认情况下只是搁置一边8Meg 用于线程,如果超过了,那么线程开始被内核杀死。

一个人说这对 CPython 来说不是问题,因为无论如何一次只有 1 个线程在运行(因为 GIL)所以我们可以指定大量线程???这件事的真相是什么?

4

2 回答 2

15
  1. “由于 GIL,一次运行一个线程。” 嗯,有点。GIL 意味着一次只能有一个线程执行Python代码。但是,任何数量的线程都可能在执行 IO、各种其他系统调用或其他不包含 GIL 的代码。

    听起来您的线程将主要执行网络 I/O,并且任意数量的线程可以同时执行 I/O。1000 个线程的 GIL 竞争可能相当激烈,但您始终可以创建多个 Python 进程并在它们之间划分 I/O 线程(即,fork在开始之前几次)。

  2. “默认情况下,Linux 内核只为线程预留 8Meg。” 我不确定你是从哪里听到的。也许您实际听到的是“在 Linux 上,默认堆栈大小通常为 8 MiB”,这是真的。每个线程将使用 8 MiB 的地址空间用于堆栈(在 64 位上没有问题)以及用于附加内存映射和线程进程本身的内核资源。您可以使用库函数更改堆栈大小threading.stack_size,如果您有很多不进行深度调用的线程,这会有所帮助。

    >>> import threading
    >>> threading.stack_size()
    0 # platform default, probably 8 MiB
    >>> threading.stack_size(64*1024) # 64 KiB stack size for future threads
    
  3. 该线程中的其他人建议使用异步/非阻塞框架。好吧,你可以这样做。然而,在现代 Linux 内核上,多线程模型可以与异步 ( select/ poll/ epoll) I/O 多路复用技术竞争。重写代码以使用异步模型是一项非常重要的工作,因此只有在无法从线程模型中获得所需性能时,我才会这样做。如果您的线程真的在尝试模拟人类延迟(例如,大部分时间都在睡觉),那么在很多情况下异步方法实际上更慢。我不确定这是否适用于 Python,仅减少 GIL 争用就可能值得切换。

于 2012-04-27T04:08:22.837 回答
3

这两个都是部分正确的:

  • 每个线程都有一个栈,如果你创建了足够多的线程,你可以用完栈的地址空间。

  • Python 也有一个叫做 GIL 的东西,它一次只允许一个 Python 线程运行。但是,一旦 Python 代码调用 C 代码,该 C 代码就可以在另一个 Python 线程运行时运行。但是,Python 中的线程仍然是物理的,并且仍然存在堆栈空间限制。

如果您计划拥有多个连接,而不是使用多个线程,请考虑使用异步设计。Twisted在这里可能会很好用。

于 2012-04-27T04:08:30.007 回答