0

I am trying to make a program that has equivalents for another module's code.

The module has it's own thread class, with the commands 'stop', and 'sleep_for'

For example, using this module, you could do this:

 from fancyModule import *
 import time

 def yell():
     while True:
         print('HI!!!')

 mythread = Thread('HI!!!')
 time.sleep(3)
 mythread.stop()

For my problem, I am making my own file, to be imported instead of the fancy one.

I can use threading, multiprocessing, asyncio, or just about anything. The problem that I am running into, is that the users function can be just about anything, so I can't use events or flags. Is there anything I can do? Maybe by stopping the thread/process from the function? Here is what I have tried:

class Thread:
    def __init__(self, callback, *arg):
        self.MYTHREAD = threading.Thread(target=callback, args=arg)
        self.MYLOCK = threading.Lock()
        self.MYTHREAD.start()
    def stop(self):
        self.MYLOCK.acquire()
    def sleep_for(self, duration, units=MSEC):
        self.MYLOCK.acquire()
        threading.Timer(duration * units, self.MYLOCK.release).start()

Note that the thread is started when the thread is defined.

As of right now I'm trying to acquire a lock, but I don't think that that is what a lock does. Is this even possible?

1

2 Answers 2

1

You want to create a thread class with stop and sleep_for methods that can handle arbitrary user functions (including infinite loops). But you want to avoid co-operative flags. I suggest to use multiprocessing instead of threading. I've given the other explanations as comments inside the code.

import multiprocessing as mp
import time

# Define time units 
msec = 0.001
sec = 1

# sleep function for use in child processes
def sleep_for(duration, units=msec):
    # Pause the current process (child) for `duration * units` seconds.
    time.sleep(duration * units)

class Thread:
    def __init__(self, arg1, *args):
        if callable(arg1):
            self._callback = arg1
        else:
            def _print_loop():
                while True:
                    print(arg1)
                    # Auto-sleep to prevent high CPU usage
                    sleep_for(100)  # 100 msec delay
            self._callback = _print_loop
        
        self._process = mp.Process(target=self._callback, args=args)
        self._process.start()
    
    def stop(self):
        if self._process.is_alive():
            self._process.terminate()
            

Usage:

from your_module import Thread, sleep_for  # Import the module-level sleep_for

# Example 1: Custom function with internal sleep
def yell():
    while True:
        print('HI!!!')
        sleep_for(500)  # Child pauses for 500 msec

my_thread1 = Thread(yell)
time.sleep(3)  # Main waits 3 seconds
my_thread1.stop()

# Example 2: String-based thread (auto-uses sleep)
my_thread2 = Thread('Hello!')  # Auto-wrapped function includes sleep
time.sleep(2)
my_thread2.stop()

# Example 3: Sleep explicitly in child
def controlled_yell():
    while True:
        print('CONTROLLED!')
        sleep_for(2, sec)  # Child pauses for 2 seconds

my_thread3 = Thread(controlled_yell)
time.sleep(6)  # Main waits 6 seconds
my_thread3.stop()

Edit: Removed sleep_for function from the class Thread and declared it as module level.

Sign up to request clarification or add additional context in comments.

4 Comments

You sleep in caller thread, not pausing the created process as it required in description.
@Mullo, Edited now. Kindly check
Thank you for all your help, it looks like everything should work, except for the sleep_for command. It still has to be part of the thread module, as in my_thread1.sleep_for(). The first example you gave wouldn't have paused the thread, and instead would have just paused the main thread. The goal is to define a thread, and then from outside of it, stop it or pause it.
😇 🤔 Am I confused. Kindly wait for few hours. Let me sleep and come back.
0

Up to Python 3.13 (the current one) There is no way in Python to pause a given thread, unless one's own code has slots that will verify for a condition and do the pause.

In the same process, the most natural way to do that is by asyncio programming - every await is a "slot" where one can insert arbitrary code, and a synchronous sleep would pause the entire async loop. But again, the running code should be an async function, with at least an await expression in its loop - not unlike having to check a Queue or Event object from time to time.

Subprocess approach

@Suramuthu's approach of running the target code in a subprocess can work - but not in the way (currently) described in the answer: the subprocess can have a controler thread to pick signals (through a multiprocessing.Queue or otherwise), and start the user code - but nonetheless, it can't pause the running code with time.sleep: one would be in the exact same situation as when running the code in another thread in the first process.

However, it is possible to send O.S. level signals to the subprocess - and these, unlike threads, can be controlled by externally running code.

The signal.SIGSTOP and signal.SIGCONT signals can be used to pause the whole process and could be easy to use. Note that these are not available under Windows though - I believe it is possible to register a signal handler for ordinary SIGKILL (or other) in windows to "fake" a process stop until a second SIGKILL is sent. For Linux and Mac, SIGSTOP and SIGCONT makes things a lot easier, as the OS itself processes them.

Even if you want to run just one process/thread at a time it may be interesting to use concurrent.futures.ProcessPoolExecutor as it will handle communications (queue), return value, subprocess lifetime for you, including all edge cases, with minimal fuss.

(/me goes to the REPL for a PoC)

Ok - it worked as planned, and I got a beautiful gitch-related ASCII art worth posting as part of the research:

In [1]: import time, os, signal; from concurrent.futures import ProcessPoolExecutor
In [4]: def counter(n):
   ...:     for i in range(n):
   ...:         time.sleep(1)
   ...:         print(i)
   ...:     return i
   ...: 

# ... setup executor with 1 worker:

In [5]: t = executor.submit(counter, 15)

# retrieve its worker PID:

In [15]: pid, proc = next(iter(executor._processes.items()))

# submit a call to counter up to 15

In [17]: t = executor.submit(counter, 15)

# type the pause sequence while counter is running - the fun part:
# ( I type os.kill(os.getpid(), signal.SIGSTOP) - but this is 
# my terminal :-D   :


In [18]: o
s1
.k2
ill3
(4
pi5
d, 6
signal7
.SI8
GST9
(P                  )10
In [18]: os.kill(pid, signal.SIGSTOP)

# counter function is now paused - I have the terminal for me!

In [19]: os.kill(pid, signal.SIGCONT)
11

In [20]: 12
13
14
In [20]: t.result()
Out[20]: 14

In [21]: 

Now, going back to your snippet and applying this:

import os, signal,time
from concurrent.futures import ProcessPoolExecutor
from threading import Timer

MSEC="MSEC"

def _warmerfunc():
    time.sleep(0)

class Thread:
    def __init__(self, callback, *args):
        self.timer = None
        self.executor = ProcessPoolExecutor(1)
        
        # start executor with a "nop" call so the worker
        # is created:
        self.executor.submit(_warmerfunc)
        
        # retrieve the dict key for the only worker - which is the PID
        # (although this is a private implementation detail
        # of ProcessPoolExecutor, it is not likely changing anytime
        # soon. Might not work for pypy, though)
        self.pid = next(iter(self.executor._processes))
        
        self.future = self.executor.submit(callback, *args)
        
    def stop(self):
        os.kill(self.pid, signal.SIGSTOP)
        
        
    def resume(self):
        self.timer = None
        os.kill(self.pid, signal.SIGCONT)
        
    def sleep_for(self, duration, units=MSEC):
        # code for handling units - normalize duration to seconds
        ...
        # ...
        self.stop()
        self.timer = Timer(duration, self.resume)
        self.timer.start()
        
    def done():
        return self.future.done()
    
    def exception():
        return self.future.exception()
        
    def result():
        return self.future.result()
        
    def __del__(self):
        self.executor.shutdown()
       

Possible same process-approach from Python 3.14 on:

With Python 3.14 (in beta, to be released in October 2025) it may be possible to pause a thread in the same process using the PEP 768 approach to insert a function call inside the another thread of a program. It will still involve a subprocess, but the called code will be running in the same process that causes the pause - therefore some limits for subprocesses (like non serializable objects) can be overcome.

The main limitation is that although it becomes possible to attach an arbitrary call to a running thread with no stop or check points, it only works with the main thread - which means that you have to run your code, including the caller code, in a secondary thread, and run the "pausable" code in the main thread - that is no trivial exercise.

Maybe it is possible to use, from ctypes, the same hooks that sys.remote_exec uses in the Python interpreter and have this working for other threads - but it would be complex.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.