Python Daemon Thread Clean Up Logic on Abrupt sys.exit()
Using Linux and Python 2.7.6, I have a script that uploads lots of files at one time. I am using multi-threading with the Queue and Threading modules.
I implemented a handler for SIGINT to stop the script if the user hits ctrl-C. I prefer to use daemon threads so I don't have to clear the queue, which would require alot of re-writing code to make the SIGINT handler have access to the Queue object since the handlers don't take parameters.
To make sure the daemon threads finish and clean up before sys.exit(), I am using threading.Event() and threading.clear() to make threads wait. This code seems to work as print threading.enumerate() only shows the main thread before the script terminates when I did debugging. Just to make sure, I was wondering if there is any kind of insight to this clean up implementation that I might be missing even though it seems to be working for me:
def signal_handler(signal, frame):
global kill_received
kill_received = True
msg = (
"\n\nYou pressed Ctrl+C!"
"\nYour logs and their locations are:"
"\n{}\n{}\n{}\n\n".format(debug, error, info))
logger.info(msg)
threads = threading.Event()
threads.clear()
while True:
time.sleep(3)
threads_remaining = len(threading.enumerate())
print threads_remaining
if threads_remaining == 1:
sys.exit()
def do_the_uploads(file_list, file_quantity,
retry_list, authenticate):
"""The uploading engine"""
value = raw_input(
"\nPlease enter how many concurent "
"uploads you want at one time(example: 200)> ")
value = int(value)
logger.info('{} concurent uploads will be used.'.format(value))
confirm = raw_input(
"\nProceed to upload files? Enter [Y/y] for yes: ").upper()
if confirm == "Y":
kill_received = False
sys.stdout.write("\x1b[2J\x1b[H")
q = CustomQueue()
def worker():
global kill_received
while not kill_received:
item = q.get()
upload_file(item, file_quantity, retry_list, authenticate, q)
q.task_done()
for i in range(value):
t = Thread(target=worker)
t.setDaemon(True)
t.start()
for item in file_list:
q.put(item)
q.join()
print "Finished. Cleaning up processes...",
#Allowing the threads to cleanup
time.sleep(4)
def upload_file(file_obj, file_quantity, retry_list, authenticate, q):
"""Uploads a file. One file per it's own thread. No batch style. This way if one upload
fails no others are effected."""
absolute_path_filename, filename, dir_name, token, url = file_obj
url = url + dir_name + '/' + filename
try:
with open(absolute_path_filename) as f:
r = requests.put(url, data=f, headers=header_collection, timeout=20)
except requests.exceptions.ConnectionError as e:
pass
if src_md5 == r.headers['etag']:
file_quantity.deduct()
Asked by: Alberta339 | Posted: 27-01-2022
Answer 1
If you want to handle Ctrl+C
; it is enough to handle KeyboardInterrupt
exception in the main thread. Don't use global X
in a function unless you do X = some_value
in it. Using time.sleep(4)
to allow the threads to cleanup is a code smell. You don't need it.
I am using threading.Event() and threading.clear() to make threads wait.
This code has no effect on your threads:
# create local variable
threads = threading.Event()
# clear internal flag in it (that is returned by .is_set/.wait methods)
threads.clear()
Don't call logger.info()
from a signal handler in a multithreaded program. It might deadlock your program. Only a limited set of functions can be called from a signal handler. The safe option is to set a global flag in it and exit:
def signal_handler(signal, frame):
global kill_received
kill_received = True
# return (no more code)
The signal might be delayed until q.join()
returns. Even if the signal were delivered immediately; q.get()
blocks your child threads. They hang until the main thread exits. To fix both issues, you could use a sentinel to signal child processes that there are no more work, drop the signal handler completely in this case:
def worker(stopped, queue, *args):
for item in iter(queue.get, None): # iterate until queue.get() returns None
if not stopped.is_set(): # a simple global flag would also work here
upload_file(item, *args)
else:
break # exit prematurely
# do child specific clean up here
# start threads
q = Queue.Queue()
stopped = threading.Event() # set when threads should exit prematurely
threads = set()
for _ in range(number_of_threads):
t = Thread(target=worker, args=(stopped, q)+other_args)
threads.add(t)
t.daemon = True
t.start()
# provide work
for item in file_list:
q.put(item)
for _ in threads:
q.put(None) # put sentinel to signal the end
while threads: # until there are alive child threads
try:
for t in threads:
t.join(.3) # use a timeout to get KeyboardInterrupt sooner
if not t.is_alive():
threads.remove(t) # remove dead
break
except (KeyboardInterrupt, SystemExit):
print("got Ctrl+C (SIGINT) or exit() is called")
stopped.set() # signal threads to exit gracefully
I've renamed value
to number_of_threads
. I've used explicit threads set
If an individual upload_file()
blocks then the program won't exit on Ctrl-C
.
Your case seems to be simple enough for multiprocessing.Pool
interface:
from multiprocessing.pool import ThreadPool
from functools import partial
def do_uploads(number_of_threads, file_list, **kwargs_for_upload_file):
process_file = partial(upload_file, **kwargs_for_upload_file)
pool = ThreadPool(number_of_threads) # number of concurrent uploads
try:
for _ in pool.imap_unordered(process_file, file_list):
pass # you could report progress here
finally:
pool.close() # no more additional work
pool.join() # wait until current work is done
It should gracefully exit on Ctrl-C
i.e., uploads that are in progress are allowed to finish but new uploads are not started.
Similar questions
Python sys.exit() help?
I am working in Python and am trying to have a line of code execute and after it executes I am calling sys.exit() to have the script itself "exit." However, it seems that sys.exit() is executing before the line of code above it executes. Below is the code I am trying to implement:
if something == True:
self.redirect('http://www.google.com/')
sys.exit()
The...
python - Issue with sys.exit() in pygame
I am learning to use Pygame, and when I use sys.exit(), I run into a problem. Here is the code:
import pygame, sys,os
from pygame.locals import *
pygame.init()
window = pygame.display.set_mode((468, 60))
pygame.display.set_caption('Game')
screen = pygame.display.get_surface()
file_name = os.path.join("data","image.bmp")
surface = pygame.image.load(file_name)
screen.blit(surface, (0,0)...
How to use sys.exit() in Python
player_input = '' # This has to be initialized for the loop
while player_input != 0:
player_input = str(input('Roll or quit (r or q)'))
if player_input == q: # This will break the loop if the player decides to quit
print("Now let's see if I can beat your score of", player)
break
if player_input != r:
print('invalid choice, try again')
if player_input ==r:
roll...
python - Is it possible for a unit test to assert that a method calls sys.exit()?
I have a Python 2.7 method that sometimes calls
sys.exit(1)
Is it possible to make a unit test that verifies this line of code is called when the right conditions are met?
python - Try and Except catch all errors, except for sys.exit()
I have created a function, but errors could pop up. That is why I want to use exceptions to generalize all errors into the same message.
However, this function contains multiple sys.exit() calls.
As a result, I would like to have my code jump into the except handler if an error was raised, unless it is caused by sys.exit(). How do I do this?
try:
myF...
python script doesn't stop with sys.exit()
In my Program I have a GUI with a progressbar. The value of the progressbar is a variable of a other script so I created an update function that refreshes the GUI so I have a moving progressbar.
def update(lblhowfar,bar,window,Zeitraum,auswahl,testprogress):# update function to refresh the progress bar and the percentage label
while rd.filtering_done==False:
Progress=int(rd.Zähler/rd.Nenner)
...
python - How do I use sys.exit()?
How do I use sys.exit() in this while loop?
The user inputs 3 sides of a triangle. If it is a right angle Pythagorean triangle, then it prints.
If the user enters "0", the program ends.
Also, is there a better way to write the infinite while loop besides using count = 0 and never incrementing it?
def pythagoreanTriple():
count = 0
print("input 3 sides of a triang...
python - I can't catch sys.exit()
I want to prevent a python program to quit when it calls sys.exit, but the following solution doesn't work:
Python 3.9.1 (tags/v3.9.1:1e5d33e, Dec 7 2020, 17:08:21) [MSC v.1927 64 bit (AMD64)] on win32
>>> import sys
>>> try:
... sys.exit()
... except SystemExit:
... print('asd')
...
Process finished with exit code 0
I've also tried BaseExcep...
python - using sys.exit() and heroku threw an error
2022-02-21T01:07:54.352381+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=_botname.herokuapp.com request_id=88df74f6-a5a1-4ee9-a0a5-c98a22a60949 fwd="115.66.143.26" dyno= connect= service= status=503 bytes= protocol=https
2022-02-21T01:07:54.988568+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=...
python - Unittest causing sys.exit()
No matter what I do sys.exit() is called by unittest, even the most trivial examples. I can't tell if my install is messed up or what is going on.
IDLE 1.2.2 ==== No Subprocess ====
>>> import unittest
>>>
>>> class Test(unittest.TestCase):
def testA(self):
a = 1
self.assertEqual(a,1)
>>> unittest.main()
option -n not recognized
Usa...
python - Handle sys.exit() in cherrypy service
When you start/stop a python cherrypy service (compiled with py2exe), this works fine When I get a sys.exit() call (from my error handler), cherrypy quits, but the service remains hanging.
Code:
import cherrypy
import win32serviceutil
import win32service
import sys
SERVICE = None
class HelloWorld:
""" Sample request handler class. """
def __init__(self):
self.iVal = ...
linux - How to bypass the 0-255 range limit for sys.exit() in python?
In python (on a Linux system), I'm launching a command using os.system() and retrieving the return code. If that return code is different from 0, I would like to make the program exit with that same return code. So I wrote:
ret = os.system(cmd)
if ret != 0:
print "exit with status %s" % ret
sys.exit(ret)
When the return code is lower than 256, it works fine, but when it'...
Python sys.exit() help?
I am working in Python and am trying to have a line of code execute and after it executes I am calling sys.exit() to have the script itself "exit." However, it seems that sys.exit() is executing before the line of code above it executes. Below is the code I am trying to implement:
if something == True:
self.redirect('http://www.google.com/')
sys.exit()
The...
mocking - How to get around "sys.exit()" in python nosetest?
It seems that python nosetest will quit when encountered sys.exit(), and mocking of this builtin doesn't work.
import - python: Importing scipy.io seems to prevent sys.exit() to work properly
Under Windows, executing the following Python script I get an ERRORLEVEL of 0 instead of the expected 2.
import sys
import scipy.io
sys.exit(2)
If I remove the import scipy.io, I got the correct ERRORLEVEL of 2...
Any idea why importing scipy.io cause such an issue?
PS: windows 7, python 2.72, scipy 0.10.1
Calling help(sys.exit) just before the sys.exit call return...
python - Issue with sys.exit() in pygame
I am learning to use Pygame, and when I use sys.exit(), I run into a problem. Here is the code:
import pygame, sys,os
from pygame.locals import *
pygame.init()
window = pygame.display.set_mode((468, 60))
pygame.display.set_caption('Game')
screen = pygame.display.get_surface()
file_name = os.path.join("data","image.bmp")
surface = pygame.image.load(file_name)
screen.blit(surface, (0,0)...
python - Why are the methods sys.exit(), exit(), raise SystemExit not working?
I need an alternative to kill the python script while inside a thread function. My intention is killing the server when the client enters a 0... Is this not working because the threads haven't been terminated? Here is my code:
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
print 'Socket created'
try:
socket.bind((HOST, PORT))
except socket.error, message:
print 'Bind statement failed. ...
How to use sys.exit() in Python
player_input = '' # This has to be initialized for the loop
while player_input != 0:
player_input = str(input('Roll or quit (r or q)'))
if player_input == q: # This will break the loop if the player decides to quit
print("Now let's see if I can beat your score of", player)
break
if player_input != r:
print('invalid choice, try again')
if player_input ==r:
roll...
python - Is it possible for a unit test to assert that a method calls sys.exit()?
I have a Python 2.7 method that sometimes calls
sys.exit(1)
Is it possible to make a unit test that verifies this line of code is called when the right conditions are met?
Still can't find your answer? Check out these communities...
PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python