什么是python线程

时间:2011-12-24 08:37:48

标签: python multithreading

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

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

    线程可以标记为“守护程序线程”。这个的意义   flag是只有守护进程线程时整个Python程序退出   离开了。

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

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

4 个答案:

答案 0 :(得分:26)

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

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

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


  4. 守护程序线程标志由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()
    

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

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

    所有线程都是流程的一部分。


      

    我的解释/理解是:主线程终止时   非守护程序线程终止。

         

    所以python守护程序线程不是python程序的一部分,如果“整个   当只有守护程序线程留下“?

    时,Python程序退出

    您的理解不正确。对于操作系统,进程由许多线程组成,所有线程都是相同的(除了C运行时在_exit的{​​{1}}添加调用之外,操作系统的主线程没有什么特别之处。 {1}}功能)。操作系统不知道守护程序线程。这纯粹是一个Python概念。

    Python解释器使用本机线程来实现Python线程,但必须记住创建的线程列表。并且使用其main挂钩,它确保atexit函数仅在最后一个非守护程序线程终止时返回到OS。使用“整个Python程序”时,文档引用整个过程。


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

    _exit

    如果使用'--use_daemon'执行此程序,您将看到程序将只打印少量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:]) 行。如果没有此标志,即使主线程完成,程序也不会终止,程序将打印Working hard行直到它被杀死。

答案 1 :(得分: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()
  1. 使用ps -o cmd,nlwp <pid>报告的线程数是NUM_THREADS+1(主线程还有一个),所以只要操作系统工具检测到线程数,就应该是OS线程。我尝试使用cpython和jython,尽管在jython中还有一些其他线程正在运行,但对于我添加的每个额外线程,ps会将线程数增加一。

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

  3. 我在开始主题之前添加了以下行:

    thread.daemon = True
    

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

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

答案 2 :(得分:4)

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

  2. 不,您只是看到了您的操作系统的底层线程实现。这种行为是通过* nix类似pthread的线程暴露出来的,或者即使是Windows也会通过这种方式实现线程。

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

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

答案 3 :(得分:0)

这个问题有很好的答案,但我觉得守护程序线程问题仍然没有以简单的方式解释。所以这个答案只涉及第三个问题

  

“当所有非守护程序线程终止时,主线程终止。”

     

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

如果您考虑守护进程是什么,通常是服务。一些代码在无限循环中运行,提供请求,填充队列,接受连接等。其他线程使用它。单独运行时没有任何意义(在单个流程中)。

因此程序不能等待守护程序线程终止,因为它可能永远不会发生。当所有非守护程序线程完成后,Python将结束该程序。它还会停止守护程序线程

要等到守护程序线程完成其工作,请使用daemon_thread.join()方法。 join()将使Python在退出之前等待守护程序线程。 function calculate(interest){ var vatti = interest.value; if (vatti != '') { var Total = vatti - 25; document.getElementById("int_amount").value = Total; }else{ document.getElementById("int_amount").value = ''; } }也接受超时参数。