如果我不join()一个python线程会发生什么?

kmb7vmvb  于 2023-11-20  发布在  Python
关注(0)|答案(4)|浏览(142)

我有一个查询。我见过开发人员编写类似代码的示例,如下所示:

import threading
def do_something():
    return true
t = threading.Thread(target=do_something)
t.start()
t.join()

字符串
我知道join()会通知解释器等待线程完全执行,但是如果我不写t.join()呢?线程会自动关闭,以后会被重用吗?
这是我第一次尝试用Python 3.5.0创建多线程应用程序。

djp7away

djp7away1#

Python线程只是一个普通的操作系统线程。如果你不加入它,它仍然会与当前线程并发运行。当目标函数完成或引发异常时,它最终会死亡。不存在“线程重用”这样的事情,一旦它死亡,它就会安息。
除非线程是一个“守护线程”(通过构造函数参数daemon或分配daemon属性),否则它将在程序退出前被隐式连接,否则,它会被突然杀死。
在Python中编写多线程程序时需要记住的一件事是,由于臭名昭著的Global interpreter lock,它们的使用有限。简而言之,使用线程不会使CPU密集型程序更快。它们只有在执行涉及等待的操作时才有用(例如,等待线程中发生某些文件系统事件)。

yb3bgrhw

yb3bgrhw2#

join部分意味着主程序将等待线程结束后再继续。如果没有join,主程序将结束,线程将继续。
现在,如果您将daemon参数设置为“True”,则意味着线程将依赖于主程序,并且如果主程序在此之前结束,则线程将结束。
这里有一个例子来更好地理解:

import threading
import time

def do_something():
    time.sleep(2)
    print("do_something")
    return True

t = threading.Thread(target=do_something)
t.daemon = True # without the daemon parameter, the function in parallel will continue even your main program ends
t.start()
t.join() # with this, the main program will wait until the thread ends
print("end of main program")

字符串
没有守护进程,没有加入:

end of main program
do_something


仅daemon:

end of main program


仅加入:

do_something
end of main program


daemon和join:

do_something
end of main program
# Note : in this case the daemon parameter is useless

chhkpiq4

chhkpiq43#

  • 如果没有**join()**,非守护进程线程将运行,并与主线程并发完成。
  • 如果没有**join()**,守护线程将与主线程并发运行,当主线程完成时,如果守护线程仍在运行,则守护线程将退出而不完成。

你可以在this post中看到我的答案详细解释。

unhi4e5o

unhi4e5o4#

在Python 3.10.12中,如果你不使用join()线程,没有什么不好的事情发生。为了测试,我使用了以下代码:

#! /usr/bin/python3

import logging
import threading
import time

def thread_function(name):
    logging.info("Thread %2s: starting", name)
    time.sleep(20)
    logging.info("Thread %2s: finishing", name)

def daemon_thread_function():
    logging.info("Daemon   : starting")
    i = 0
    while True:
        time.sleep(1)
        i = i + 1
        logging.info("Daemon   : %s seconds have passed", i)

if __name__ == "__main__":
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt="%H:%M:%S")

    for i in range(1, 51):
        logging.info("Main     : creating and running thread %2s", i)
        threading.Thread(target=thread_function, args=(i,)).start()

    logging.info("Main     : creating and running daemon thread")
    threading.Thread(target=daemon_thread_function, args=(), daemon=True).start()

    wait = 40
    logging.info("Main     : sleeping %s seconds", wait)
    time.sleep(wait)
    logging.info("Main     : finished waiting")

字符串
在启动此代码后的前20秒内,您可以使用ps -eLfls /proc/MAINPROCESSID/task和许多其他命令来验证,在此测试应用程序中实际上有52个线程正在运行:主线程,50个前台子线程和守护线程。启动应用程序后20秒,前台子线程全部退出,只剩下两个线程:主线程和守护线程. ps -eLf没有显示任何未读状态或类似状态的“僵尸线程”,因此没有操作系统资源被未加入的线程阻塞!
当总时间超过40秒时,主线程也退出,守护线程也完成,python应用程序退出。因此未加入的线程也不会阻塞Python内部的任何资源,特别是,它们不会让守护线程不必要地运行。
只有一个建议:如果你打算永远不加入线程,不要保留对周围创建的线程的引用,这样python解释器就可以完成它的工作,并垃圾收集与所有完成的线程相关的所有资源。测试应用程序threading.Thread(target=..., args=...).start()中的语法是实现这种“发射并忘记”语义的一种可能方法。
当然,请确保使用其他同步方法(如threading.mutexthreading.semaphore)来跟踪线程的工作。

相关问题