Python多线程退出工作线程

时间:2021-09-11 20:42:36

So I have the main thread spawning worker threads that have infinite loops to deal with system monitoring. So it looks something like this

所以我有主线程产生工作线程,它具有无限循环来处理系统监视。所以它看起来像这样

while True:
     Check_server_status( host )
     wait( *minutes* )

This worker thread should run forever because we need to constantly monitor the servers and each thread is currently monitoring one machine but I may scale it so each thread has a list of servers to check on. Also, each thread is writing to a csv file the information that it is finding.

这个工作线程应该永远运行,因为我们需要不断监视服务器,并且每个线程当前正在监视一台机器,但我可以对其进行扩展,以便每个线程都有一个要检查的服务器列表。此外,每个线程都在向csv文件写入它正在查找的信息。

The main thread just calls this thread for each host that it finds in a list.

主线程只为它在列表中找到的每个主机调用此线程。

hosts = [a,b]
threads = []
for host in hosts:
     t = worker( host )
     t.daemon = True
     t.start()
     threads.append( t )

I am trying to make this script exit cleanly on ctrl-c. So I want to make sure that the files are closed and that the threads exits. Is there any good way to handle this?

我试图让这个脚本在ctrl-c上干净地退出。所以我想确保文件已关闭并且线程退出。有没有什么好方法可以解决这个问题?

Thanks in advance for the help!

在此先感谢您的帮助!

1 个解决方案

#1


0  

Well, for starters, daemon threads terminated by the main thread ending aren't cleaned up properly, so avoid that.

好吧,对于初学者来说,由主线程结束终止的守护进程线程没有正确清理,所以避免这样做。

And KeyboardInterrupt isn't necessarily delivered to the main thread, or to any specific thread, so it's probably not best to rely on Python's default handler for SIGINT here, but instead write your own handler to replace it.

并且KeyboardInterrupt不一定被传递到主线程或任何特定线程,所以在这里依赖Python的默认处理程序可能不是最好,而是编写自己的处理程序来替换它。

Probably the simplest approach is to have all the threads loop on a shared threading.Event object, looping based on a .wait(*seconds*) call (so your per loop sleep is folded into the while condition, and you can still exit immediately, anytime during the wait).

可能最简单的方法是让所有线程在共享的threading.Event对象上循环,基于.wait(* seconds *)调用循环(这样你的每个循环睡眠被折叠到while条件,你仍然可以立即退出,等待期间的任何时候)。

So you might do:

你可能这样做:

import threading

shouldexit = threading.Event()

Then your worker functions would be of the form:

那么你的工作函数将是以下形式:

def worker(host):
    while not shouldexit.wait(minutes * 60):
        Check_server_status( host )

They'd all be launched without setting them as daemons, and you'd register a SIGINT handler like this one:

它们都是在没有将它们设置为守护进程的情况下启动的,并且您将注册一个像这样的SIGINT处理程序:

import signal

def ctrlchandler(signum, frame):
    print('User triggered quit')
    shouldexit.set()

# Set the signal handlers for the Ctrl-C related events
if hasattr(signal, 'CTRL_C_EVENT'):
    # Only on Windows
    signal.signal(signal.CTRL_C_EVENT, ctrlchandler)
signal.signal(signal.SIGINT, ctrlchandler)

Ideally, there would be no living main thread to kill off in this case (all threads would be looping on the shared Event, with the main thread exiting after launching the workers); if that's not the case, you'd need to figure out some way of terminating the main thread.

理想情况下,在这种情况下,没有生命主线程可以终止(所有线程都将在共享事件上循环,主线程在启动工作线程后退出);如果不是这种情况,你需要找出终止主线程的方法。

One approach might be to store off the default handler for SIGINT when you register your replacement handler, have your handler join all the worker threads after setting the Event, then explicitly invoke the original SIGINT handler so KeyboardInterrupt fires in a surviving thread as normal and cleans up the now workerless main thread.

一种方法可能是在注册替换处理程序时存储SIGINT的默认处理程序,让您的处理程序在设置Event后加入所有工作线程,然后显式调用原始SIGINT处理程序,以便KeyboardInterrupt在正常情况下在正常运行的线程中触发并清除现在没有工作的主线程。

#1


0  

Well, for starters, daemon threads terminated by the main thread ending aren't cleaned up properly, so avoid that.

好吧,对于初学者来说,由主线程结束终止的守护进程线程没有正确清理,所以避免这样做。

And KeyboardInterrupt isn't necessarily delivered to the main thread, or to any specific thread, so it's probably not best to rely on Python's default handler for SIGINT here, but instead write your own handler to replace it.

并且KeyboardInterrupt不一定被传递到主线程或任何特定线程,所以在这里依赖Python的默认处理程序可能不是最好,而是编写自己的处理程序来替换它。

Probably the simplest approach is to have all the threads loop on a shared threading.Event object, looping based on a .wait(*seconds*) call (so your per loop sleep is folded into the while condition, and you can still exit immediately, anytime during the wait).

可能最简单的方法是让所有线程在共享的threading.Event对象上循环,基于.wait(* seconds *)调用循环(这样你的每个循环睡眠被折叠到while条件,你仍然可以立即退出,等待期间的任何时候)。

So you might do:

你可能这样做:

import threading

shouldexit = threading.Event()

Then your worker functions would be of the form:

那么你的工作函数将是以下形式:

def worker(host):
    while not shouldexit.wait(minutes * 60):
        Check_server_status( host )

They'd all be launched without setting them as daemons, and you'd register a SIGINT handler like this one:

它们都是在没有将它们设置为守护进程的情况下启动的,并且您将注册一个像这样的SIGINT处理程序:

import signal

def ctrlchandler(signum, frame):
    print('User triggered quit')
    shouldexit.set()

# Set the signal handlers for the Ctrl-C related events
if hasattr(signal, 'CTRL_C_EVENT'):
    # Only on Windows
    signal.signal(signal.CTRL_C_EVENT, ctrlchandler)
signal.signal(signal.SIGINT, ctrlchandler)

Ideally, there would be no living main thread to kill off in this case (all threads would be looping on the shared Event, with the main thread exiting after launching the workers); if that's not the case, you'd need to figure out some way of terminating the main thread.

理想情况下,在这种情况下,没有生命主线程可以终止(所有线程都将在共享事件上循环,主线程在启动工作线程后退出);如果不是这种情况,你需要找出终止主线程的方法。

One approach might be to store off the default handler for SIGINT when you register your replacement handler, have your handler join all the worker threads after setting the Event, then explicitly invoke the original SIGINT handler so KeyboardInterrupt fires in a surviving thread as normal and cleans up the now workerless main thread.

一种方法可能是在注册替换处理程序时存储SIGINT的默认处理程序,让您的处理程序在设置Event后加入所有工作线程,然后显式调用原始SIGINT处理程序,以便KeyboardInterrupt在正常情况下在正常运行的线程中触发并清除现在没有工作的主线程。