-2

This seems like a particularly confusing question based on the other similar answers I found on SO. I have code similar to the following:

def parentFunction():
    # Other code
    while True:
        var1, var2 = anotherFunction1() # Getting client details after listening on open port
        threading.Thread(target = anotherFunction2, args=(var1, var2)).start()
        childFunction(var1,var2)
        print("PRINT #1: Running in Parent Function") # This only prints once for some reason

def childFunction(var1, var2):
    threading.Timer(10, childFunction, args=(var1,var2)).start()
    print("PRINT #2: Running in child function") # Prints every 10 seconds
    
    # Other code 

    if (someConditionIsMet):
        print("PRINT #3: Exiting") 
        end_process_and_exit_here()

So basically, when I ran the parentFunction(), I would go into a neverending loop where ever 10 seconds, my console would print "PRINT #2: Running in child function". When the someConditionIsMet was true, my console would print "PRINT #3: Exiting" but then it wouldn't exit. Hence, my loop would carry on forever. I am not sure if it's relevant, but parts of the code has a Threading.Lock as well.

Where I have written end_process_and_exit_here() above, I tried using several methods to kill a thread such as

  1. Raising exceptions and setting flags - These assume that I have started my thread outside of my loop so it's not comparable.
  2. Even this qn about looping threads assumes the thread isnt being looped
  3. Killing using join or stop - stop() was not an option I could access. join() was available but it didn't work i.e. after it was called, the next thread (PRINT #2) continued printing.
  4. Other answers suggesting the use of signals (1) (2), also didn't work.
  5. Using sys.exit() or break in different parts of my code also did not result in the threads stopping.

Is there any method for me to easily exit from such a looping thread?

Note: I need to use threading and not multiprocessing.

codingray
  • 106
  • 7
  • There's quite some handwaving involved in your code, can you extract a [mcve]? In any case, starting threads in an endless loop seems like a bad idea. – Ulrich Eckhardt Oct 31 '21 at 12:12

2 Answers2

0

You could use python-worker, simply add @worker above you function pip install python-worker

from worker import worker

@worker
def anotherFunction2(var1,var2):
   # your code here
   pass

@worker
def parentFunction():
    # Other code
    while True:
        var1, var2 = anotherFunction1() # Getting client details after listening on open port
        function2Worker = anotherFunction2(var1,var2) # this will automatically run as thread since you put @worker above your function
        childFunction(var1,var2)
        print("PRINT #1: Running in Parent Function") # This only prints once for some reason

def childFunction(var1, var2):
    parentWorker = parentFunction(var1, var2)
    
    # Other code 

    if (someConditionIsMet):
        parentWorker.abort()
danangjoyoo
  • 350
  • 1
  • 6
-1

So as an update, I have managed to resolve this issue. The problem with the other answer stated by me (shown below) is that just .cancel() by itself only seemed to work for one timer thread. But as can be seen in the problem, childFunction() itself calls childFunction() and can also be called by the parentFunction, meaning that there may be multiple timer threads.

What worked for my specific case was naming my threads as below:

t1 = threading.Timer(10, childFunction, args=(var1,var2,number))
t1.name = t1.name + "_timer" + str(number)
t1.start()

Thereafter, I could cancel all timer threads that were created from this process by:

for timerthread in threading.enumerate():
    if timerthread.name.endswith('timer' + str(number)):
        timerthread.cancel()

Below is the ORIGINAL METHOD I USED WHICH CAUSED MANY ISSUES:

I'm not certain if this is a bad practice (in fact I feel it may be based on the answers linked in the question saying that we should never 'kill a thread'). I'm sure there are reasons why this is not good and I'd appreciate anyone telling me why. However, the solution that ultimately worked for me was to use .cancel().

So first change would be to assign your thread Timer to a variable instead of calling it directly. So instead of threading.Timer(10, childFunction, args=(var1,var2)).start(), it should be

t = threading.Timer(10, childFunction, args=(var1,var2))
t.start()

Following that, instead of end_process_and_exit_here(), you should use t.cancel(). This seems to work and stops all threads mid-process. However, the bad thing is that it doesn't seem to carry on with other parts of the program.

codingray
  • 106
  • 7
  • what does assignment to a variable even change? because that is literally the same as calling it in one line (except now it would be 2 lines) – Matiiss Oct 25 '21 at 12:17
  • @Matiiss when I didn't assign it to a variable, the next step (`t.cancel()`) didn't work for me. So if I didn't assign it to a variable and left it as `threading.Timer(10, childFunction, args=(var1,var2)).start()`, I don't know how to apply the same `cancel()` solution. If I use `threading.Timer(10, childFunction, args=(var1,var2)).cancel()`, it doesn't do anything except keep looping in the `childFunction` method as described above. I'm sure there are better ways but I am not aware of them. – codingray Oct 25 '21 at 12:41
  • no, if that is the case, this is the way to go, pretty simple, if you need to use some object later, have a reference to it – Matiiss Oct 25 '21 at 12:42