Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pause and resume thread in python

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?

like image 762
evaleria Avatar asked Jun 14 '17 18:06

evaleria


People also ask

How do you pause a process in python?

Python sleep() function will pause Python code or delay the execution of program for the number of seconds given as input to sleep(). The sleep() function is part of the Python time module. You can make use of Python sleep function when you want to temporarily halt the execution of your code.

How do you sleep a thread in python?

Python thread sleep The time. sleep() function takes time as its argument and suspends the execution of the current thread for the number of seconds specified in the argument. In multi-threaded programs, the function suspends a thread rather than the whole process.

How do I pause a thread from another thread?

Thread. sleep() method can be used to pause the execution of current thread for specified time in milliseconds.

Can you pause a thread in python?

Can you pause threads Python? We all know that the threading module can implement multi-threading in python, but the module does not provide methods for suspending, resuming and stopping threads. Once the thread object calls the start method, it can only wait until the corresponding method function is completed.


2 Answers

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).

like image 124
Luis Masuelli Avatar answered Oct 12 '22 06:10

Luis Masuelli


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

like image 35
Eric Avatar answered Oct 12 '22 05:10

Eric