Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread that I can pause and resume?

I'm trying to create a thread, that does stuff in the background. I need to be able to effectively 'pause' it when I need to and 'resume' it again later. Also, if the thread is in the middle of doing something when I 'pause' it, it should make the calling thread wait until it finishes what it's doing.

I'm pretty new to Multithreading in Python, so I haven't gotten all that far.

What I have pretty much does everything except make the calling thread wait if pause is called while my thread is doing something.

Here's the outline of what I'm trying to achieve in code:

import threading, time

class Me(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        #flag to pause thread
        self.paused = False

    def run(self):
        while True:
            if not self.paused:
                #thread should do the thing if
                #not paused
                print 'do the thing'
                time.sleep(5)

    def pause(self):
        self.paused = True
        #this is should make the calling thread wait if pause() is
        #called while the thread is 'doing the thing', until it is
        #finished 'doing the thing'

    #should just resume the thread
    def resume(self):
        self.paused = False

I think I basically need a locking mechanism, but within the same thread?

like image 420
mickzer Avatar asked Nov 10 '15 21:11

mickzer


2 Answers

Conditions can be used for this.

Here's an example filling in your skeleton:

class Me(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        #flag to pause thread
        self.paused = False
        # Explicitly using Lock over RLock since the use of self.paused
        # break reentrancy anyway, and I believe using Lock could allow
        # one thread to pause the worker, while another resumes; haven't
        # checked if Condition imposes additional limitations that would 
        # prevent that. In Python 2, use of Lock instead of RLock also
        # boosts performance.
        self.pause_cond = threading.Condition(threading.Lock())

    def run(self):
        while True:
            with self.pause_cond:
                while self.paused:
                    self.pause_cond.wait()

                #thread should do the thing if
                #not paused
                print 'do the thing'
            time.sleep(5)

    def pause(self):
        self.paused = True
        # If in sleep, we acquire immediately, otherwise we wait for thread
        # to release condition. In race, worker will still see self.paused
        # and begin waiting until it's set back to False
        self.pause_cond.acquire()

    #should just resume the thread
    def resume(self):
        self.paused = False
        # Notify so thread will wake after lock released
        self.pause_cond.notify()
        # Now release the lock
        self.pause_cond.release()

Hope that helps.

like image 91
ShadowRanger Avatar answered Oct 13 '22 22:10

ShadowRanger


Use threading.Event instead of a boolean variable, and add another event for busy state:

def __init__(self):
    ...
    self.can_run = threading.Event()
    self.thing_done = threading.Event()
    self.thing_done.set()
    self.can_run.set()    

def run(self):
    while True:
        self.can_run.wait()
        try:
            self.thing_done.clear()
            print 'do the thing'
        finally:
            self.thing_done.set()

def pause(self):
    self.can_run.clear()
    self.thing_done.wait()

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

edit: previous answer was wrong, I fixed it and changed variable names to be clear

like image 29
mguijarr Avatar answered Oct 13 '22 23:10

mguijarr