什么是python线程

war*_*iuc 34 python multithreading

我有几个关于Python线程的问题.

  1. Python线程是Python还是OS实现?
  2. 当我使用htop时,多线程脚本有多个条目 - 相同的内存消耗,相同的命令但不同的PID.这是否意味着[Python]线程实际上是一种特殊的进程?(我知道htop中有一个设置将这些线程显示为一个进程 - Hide userland threads)
  3. 文件说:

线程可以标记为"守护程序线程".这个标志的意义在于,当只剩下守护进程线程时,整个Python程序都会退出.

我的解释/理解是:当所有非守护程序线程终止时,主线程终止.

因此,如果"当只剩下守护程序线程时整个Python程序退出",那么python守护程序线程不是Python程序的一部分?

Syl*_*sne 28

  1. Python线程是在我所知的所有实现中使用OS线程实现的(C Python,PyPy和Jython).对于每个Python线程,都有一个底层的OS线程.

  2. 某些操作系统(Linux是其中之一)显示所有正在运行的进程列表中由同一可执行文件启动的所有不同线程.这是操作系统的实现细节,而不是Python的实现细节.在某些其他操作系统上,列出所有进程时可能看不到这些线程.

  3. 当最后一个非守护程序线程完成时,该进程将终止.此时,所有守护程序线程都将终止.因此,这些线程是您的进程的一部分,但不会阻止它终止(而常规线程将阻止它).这是用纯Python实现的.当_exit调用系统函数时它会终止(它将终止所有线程),当主线程终止(或被sys.exit调用)时,Python解释器会检查是否有另一个非守护程序线程在运行.如果没有,则调用_exit,否则等待非守护程序线程完成.


守护程序线程标志由threading模块以纯Python实现.加载模块时,Thread会创建一个对象来表示主线程,并将其_exitfunc方法注册为atexit钩子.

这个函数的代码是:

class _MainThread(Thread):

    def _exitfunc(self):
        self._Thread__stop()
        t = _pickSomeNonDaemonThread()
        if t:
            if __debug__:
                self._note("%s: waiting for other threads", self)
        while t:
            t.join()
            t = _pickSomeNonDaemonThread()
        if __debug__:
            self._note("%s: exiting", self)
        self._Thread__delete()
Run Code Online (Sandbox Code Playgroud)

调用时sys.exit,或者当主线程终止时,Python解释器将调用此函数.当函数返回时,解释器将调用系统_exit函数.当只有守护进程线程运行时(如果有的话),该函数将终止.

_exit调用该函数时,操作系统将终止所有进程线程,然后终止该进程.在_exit完成所有非守护程序线程之前,Python运行库不会调用该函数.

所有线程都是该过程的一部分.


我的解释/理解是:当所有非守护程序线程终止时,主线程终止.

所以python守护程序线程不是python程序的一部分,如果"当只剩下守护程序线程时整个Python程序退出"?

你的理解是不正确的.对于OS,进程由许多线程组成,所有线程都是相同的(OS的主线程没有什么特别之处,除了C运行时_exitmain函数末尾添加一个调用).操作系统不知道守护程序线程.这纯粹是一个Python概念.

Python解释器使用本机线程来实现Python线程,但必须记住创建的线程列表.并且使用它的atexit钩子,它确保_exit只有当最后一个非守护程序线程终止时,该函数才会返回到OS.使用"整个Python程序"时,文档引用整个过程.


以下程序可以帮助理解守护程序线程和常规线程之间的区别:

import sys
import time
import threading

class WorkerThread(threading.Thread):

    def run(self):
        while True:
            print 'Working hard'
            time.sleep(0.5)

def main(args):
    use_daemon = False
    for arg in args:
        if arg == '--use_daemon':
            use_daemon = True
    worker = WorkerThread()
    worker.setDaemon(use_daemon)
    worker.start()
    time.sleep(1)
    sys.exit(0)

if __name__ == '__main__':
    main(sys.argv[1:])
Run Code Online (Sandbox Code Playgroud)

如果使用'--use_daemon'执行此程序,您将看到该程序将只打印少量Working hard行.如果没有此标志,即使主线程完成,程序也不会终止,程序将打印Working hard行直到它被杀死.


jco*_*ado 11

我不熟悉实现,所以让我们做一个实验:

import threading
import time

def target():
    while True:
        print 'Thread working...'
        time.sleep(5)

NUM_THREADS = 5

for i in range(NUM_THREADS):
    thread = threading.Thread(target=target)
    thread.start()
Run Code Online (Sandbox Code Playgroud)
  1. 使用的线程数ps -o cmd,nlwp <pid>NUM_THREADS+1(主线程的一个),因此只要OS工具检测到线程数,它们就应该是OS线程.我尝试使用cpython和jython,尽管在jython中还有一些其他线程正在运行,但对于我添加的每个额外线程,将ps线程数增加1.

  2. 我不确定htop行为,但ps似乎是一致的.

  3. 我在启动线程之前添加了以下行:

    thread.daemon = True
    
    Run Code Online (Sandbox Code Playgroud)

    当我执行使用cpython时,程序几乎立即终止并且没有找到任何进程ps,所以我的猜测是程序与线程一起终止.在jython中,程序以相同的方式工作(它没有终止),所以也许有一些来自jvm的其他线程阻止程序终止或守护程序线程不受支持.

注意:我在java1.6.0_23上使用了Ubuntu 11.10和python 2.7.2+以及jython 2.2.1


Don*_*ion 5

  1. Python 线程实际上是一个解释器实现,因为所谓的全局解释器锁(GIL),即使它在技术上使用操作系统级线程机制。在 *nix 上,它使用 pthreads,但 GIL 有效地使其成为坚持应用程序级线程范例的混合体。因此,您将在 *nix 系统上多次在 ps/top 输出中看到它,但它的行为(性能方面)仍然类似于软件实现的线程。

  2. 不,您只是看到操作系统的底层线程实现类型。这种行为是由 *nix pthread 类线程暴露的,或者我告诉,甚至 Windows 也确实以这种方式实现线程。

  3. 当程序关闭时,它也会等待所有线程完成。如果您有线程,这可能会无限期地推迟退出,那么明智的做法是将这些线程标记为“守护进程”,并允许您的程序完成,即使这些线程仍在运行。

您可能感兴趣的一些参考资料:

  • 1.这是错误的。Python 线程是操作系统线程。GIL 确实会影响多核系统的性能,但在某些情况下,您仍然可以从多核中获得一些好处(特别是当线程将大部分时间花在 C 库函数中的 cpu 绑定上时)。 (2认同)
  • @Duncan:我建议看一下 David Beazley 的精彩演讲。我出于天真而尝试使用线程模块,但由于不合逻辑、不一致的性能结果而感到沮丧。这些显然是由蟒蛇引起的。如果在多核、多处理器系统上仅使用一个附加线程,您不会期望获得更差的性能。毫无疑问,事实证明这是蟒蛇的错。事实上,Python 线程最初并不是为了提高性能,而是为了利用实用性。那很好! (2认同)