7

I need to pause and resume thread, which continuously executes some task. Execution begins when start() is called, it should not be interrupted and must continue from the point when pause() is called.

How can I do this?

evaleria
  • 1,281
  • 6
  • 23
  • 30
  • Real question is **"How to follow data and steps in a thread ?"** – dsgdfg Jun 14 '17 at 19:38
  • Does this answer your question? [How to pause / resume any external process under Windows?](https://stackoverflow.com/questions/100480/how-to-pause-resume-any-external-process-under-windows) – William Baker Morrison Dec 09 '20 at 16:40

4 Answers4

8

Please remember that using threads in Python will not grant you a parallel processing, except for the case of IO blocking operations. For more information on this, take a look at this and this

You cannot pause a Thread arbitrarily in Python (please keep that in mind before reading further). I am neither sure you have a way to do that at an OS level (e.g. by using pure-C). What you can do is allow the thread to be paused at specific points you consider beforehand. I will give you an example:

class MyThread(threading.Thread):

    def __init__(self, *args, **kwargs):
        super(MyThread, self).__init__(*args, **kwargs)
        self._event = threading.Event()

    def run(self):
        while True:
            self.foo() # please, implement this.
            self._event.wait()
            self.bar() # please, implement this.
            self._event.wait()
            self.baz() # please, implement this.
            self._event.wait()

    def pause(self):
        self._event.clear()

    def resume(self):
        self._event.set()

This approach will work but:

  • Threading is usually a bad idea, based on the links I gave you.
  • You have to code the run method by yourself, with this approach. This is because you need to have control over the exact points you'd like to check for pause, and this implies accessing the Thread object (perhaps you'd like to create an additional method instead of calling self._event.wait()).
  • The former point makes clear that you cannot pause arbitrarily, but just when you specified you could pause. Avoid having long operations between pause points.

Edit I did not test this one, but perhaps this will work without so much subclassing if you need more than one thread like this:

class MyPausableThread(threading.Thread):

    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
        self._event = threading.Event()
        if target:
            args = (self,) + args
        super(MyPausableThread, self).__init__(group, target, name, args, kwargs)

    def pause(self):
        self._event.clear()

    def resume(self):
        self._event.set()

    def _wait_if_paused(self):
        self._event.wait()

This should allow you to create a custom thread without more subclassing, by calling MyPausableThread(target=myfunc).start(), and your callable's first parameter will receive the thread object, from which you can call self._wait_if_paused() when you need to pause-check.

Or even better, if you want to isolate the target from accessing the thread object:

class MyPausableThread(threading.Thread):

    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
        self._event = threading.Event()
        if target:
            args = ((lambda: self._event.wait()),) + args
        super(MyPausableThread, self).__init__(group, target, name, args, kwargs)

    def pause(self):
        self._event.clear()

    def resume(self):
        self._event.set()

And your target callable will receive in the first parameter a function that can be called like this: pause_checker() (provided the first param in the target callable is named pause_checker).

U13-Forward
  • 69,221
  • 14
  • 89
  • 114
Luis Masuelli
  • 12,079
  • 10
  • 49
  • 87
  • If you don't mind me asking, why is threading a bad idea? It's often implemented badly (like in your case as nicely explained in [Stop Writing Classes](https://www.youtube.com/watch?v=o9pEzgHorH0)) but it's a tremendous idea for solving concurrency problems (especially for slow I/O operations) and for everything else there is multiprocessing. You properly prefaced your answer, but just because you can multiprocess doesn't mean that threading is not a great idea when you need parallelization for purposes other than processing optimization. – zwer Jun 14 '17 at 18:33
  • Well, for the cases when no I/O is involved, it is preferred to use asynchronous processing, like e.g. Tornado does. That saves you from a lot of overhead of context switching and stuff like that, since it is a non-threaded paradigm (with the same tips regarding "pausing"!). However, if a small amount of threads will be spawned, the difference will not be noticed. – Luis Masuelli Jun 14 '17 at 18:36
  • Btw I added use cases where only one new class is added. But for a thread to be paused, you need some kind of communication between the implementation, and the container (the thread). So I added a way (which required just one reusable subclassing) that can help you control the pausing. – Luis Masuelli Jun 14 '17 at 18:38
  • `asyncio` doesn't exist in Python versions prior to 3.4, and it essentially uses the same threading interface in just a little more user friendly way (with an acceptable overhead), and Tornado is a third party library - one should not need a third party library to efficiently use basic features of a language like I/O. Also, you most definitely can pause a thread by preventing GIL from context switching. – zwer Jun 14 '17 at 18:41
  • I invite you to add an answer explaining that (i.e. preventing the context switch in GIL). Regarding `asyncio`, it is not the only asynchronous processing in Python. Perhaps you never used Tornado or Twisted, but they are asynchronous frameworks and work in 2.7. Another popular asynchronous processing package is django-channels, using an asynchronous worker replacing WSGI and serving a specific purpose. So yes, there are well known asynchronous solutions which started from the exact same problem of Threads and GIL. – Luis Masuelli Jun 14 '17 at 18:45
  • 1
    @LuisMasuelli: It's a bad idea, but you _can_ pause threads arbitrarily in python. See my answer. – Eric Feb 27 '19 at 09:52
3

You can do this by attaching a trace function that causes all other threads to wait for a signal:

import sys
import threading
import contextlib

# needed to enable tracing
if not sys.gettrace():
    sys.settrace(lambda *args: None)

def _thread_frames(thread):
    for thread_id, frame in sys._current_frames().items():
        if thread_id == thread.ident:
            break
    else:
        raise ValueError("No thread found")
    # walk up to the root
    while frame:
        yield frame
        frame = frame.f_back


@contextlib.contextmanager
def thread_paused(thread):
    """ Context manager that pauses a thread for its duration """
    # signal for the thread to wait on
    e = threading.Event()

    for frame in _thread_frames(thread):
        # attach a new temporary trace handler that pauses the thread

        def new(frame, event, arg, old = frame.f_trace):
            e.wait()

            # call the old one, to keep debuggers working
            if old is not None:
                return old(frame, event, arg)
        frame.f_trace = new

    try:
        yield
    finally:
        # wake the other thread
        e.set()

Which you can use as:

import time

def run_after_delay(func, delay):
    """ Simple helper spawning a thread that runs a function in the future """
    def wrapped():
        time.sleep(delay)
        func()
    threading.Thread(target=wrapped).start()

main_thread = threading.current_thread()

def interrupt():
    with thread_paused(main_thread):
        print("interrupting")
        time.sleep(2)
        print("done")

run_after_delay(interrupt, 1)
start = time.time()
def actual_time(): return time.time() - start

print("{:.1f} == {:.1f}".format(0.0, actual_time()))
time.sleep(0.5)
print("{:.1f} == {:.1f}".format(0.5, actual_time()))
time.sleep(2)
print("{:.1f} != {:.1f}".format(2.5, actual_time()))

Giving

0.0 0.0
0.5 0.5
interrupting
done
2.5 3.0

Note how the interrupt causes the sleep on the main thread to wait longer

Eric
  • 95,302
  • 53
  • 242
  • 374
0

You can do this using Process class from psutil library.

Example:

>>> import psutil
>>> pid = 7012
>>> p = psutil.Process(pid)
>>> p.suspend()
>>> p.resume()

See this answer: https://stackoverflow.com/a/14053933

Edit: This method will suspend the whole process, not only one thread. ( I don't delete this answer, so others can know this method won't work.)

0
while(int(any) < 2000):
             sleep(20)
             print(waiting any...)
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
  • 2
    Generally, answers are much more helpful if they include an explanation of what the code is intended to do, and why that solves the problem without introducing others. – Jeroen Steenbeeke Feb 12 '21 at 07:39