29

I have a long process that I've scheduled to run in a thread, because otherwise it will freeze the UI in my wxpython application.

I'm using:

threading.Thread(target=myLongProcess).start()

to start the thread and it works, but I don't know how to pause and resume the thread. I looked in the Python docs for the above methods, but wasn't able to find them.

Could anyone suggest how I could do this?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
jimbo
  • 369
  • 1
  • 3
  • 7

6 Answers6

29

I did some speed tests as well, the time to set the flag and for action to be taken is pleasantly fast 0.00002 secs on a slow 2 processor Linux box.

Example of thread pause test using set() and clear() events:

import threading
import time

# This function gets called by our thread.. so it basically becomes the thread init...                
def wait_for_event(e):
    while True:
        print('\tTHREAD: This is the thread speaking, we are Waiting for event to start..')
        event_is_set = e.wait()
        print('\tTHREAD:  WHOOOOOO HOOOO WE GOT A SIGNAL  : %s' % event_is_set)
        # or for Python >= 3.6
        # print(f'\tTHREAD:  WHOOOOOO HOOOO WE GOT A SIGNAL  : {event_is_set}')
        e.clear()

# Main code
e = threading.Event()
t = threading.Thread(name='pausable_thread', 
                     target=wait_for_event,
                     args=(e,))
t.start()

while True:
    print('MAIN LOOP: still in the main loop..')
    time.sleep(4)
    print('MAIN LOOP: I just set the flag..')
    e.set()
    print('MAIN LOOP: now Im gonna do some processing')
    time.sleep(4)
    print('MAIN LOOP:  .. some more processing im doing   yeahhhh')
    time.sleep(4)
    print('MAIN LOOP: ok ready, soon we will repeat the loop..')
    time.sleep(2)
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Rich
  • 299
  • 3
  • 3
  • 1
    In order to answer the question, you would probably have an object thread_resume = threading.Event() and then thread_resume.set(). In your thread, you check thread_resume.wait() at convenient times. If another thread calls thread_resume.clear(), the thread will pause, and resume when another thread calls thread_resume.set() – Arglanir Oct 31 '13 at 13:42
  • 27
    These are the most excited comment and output strings I have ever seen. – Kyle Strand Dec 16 '14 at 23:39
  • 1
    But the `I just set the flag` should be after `e.set()` innit. – kyrill May 01 '19 at 18:27
10

There is no method for other threads to forcibly pause a thread (any more than there is for other threads to kill that thread) -- the target thread must cooperate by occasionally checking appropriate "flags" (a threading.Condition might be appropriate for the pause/unpause case).

If you're on a unix-y platform (anything but windows, basically), you could use multiprocessing instead of threading -- that is much more powerful, and lets you send signals to the "other process"; SIGSTOP should unconditionally pause a process and SIGCONT continues it (if your process needs to do something right before it pauses, consider also the SIGTSTP signal, which the other process can catch to perform such pre-suspension duties. (There may be ways to obtain the same effect on Windows, but I'm not knowledgeable about them, if any).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 1
    Unfortunately, I'm targeting windows, so I don't think i'll be able to use multiprocessing. I don't want one thread to pause another, I just want to send a message to the thread telling it to pause itself(or is this the same thing as telling one thread to pause another?), could that be done by tweaking the threading class? – jimbo Jul 16 '10 at 06:36
  • 1
    "Sending a message" can be done literally by a Queue, but (as I said) more practically for this case with a Condition. But the target thread must periodically check the Condition (or Queue) in order to **receive** the "messages": there is no way for you to "force" it to "receive the messages", you must _code_ it that way explicitly (likely w/a loop). There is no way for other threads to forcibly make a thread "receive messages meant for it", to cast my A's first sentence in the terms you appear to prefer. BTW, Windows does have a `SuspendThread` API (cont.) – Alex Martelli Jul 16 '10 at 15:43
  • 1
    For a Windows-specific solution, see http://msdn.microsoft.com/en-us/library/ms686345(VS.85).aspx -- you can call Windows APIs directly from Python with `ctypes` or http://sourceforge.net/projects/pywin32/ . – Alex Martelli Jul 16 '10 at 15:47
4

You can use signals: http://docs.python.org/library/signal.html#signal.pause

To avoid using signals you could use a token passing system. If you want to pause it from the main UI thread you could probably just use a Queue.Queue object to communicate with it.

Just pop a message telling the thread the sleep for a certain amount of time onto the queue.

Alternatively you could simply continuously push tokens onto the queue from the main UI thread. The worker should just check the queue every N seconds (0.2 or something like that). When there are no tokens to dequeue the worker thread will block. When you want it to start again just start pushing tokens on to the queue from the main thread again.

dpn
  • 592
  • 3
  • 9
  • 1
    No, you cannot send signals to a thread -- only to a process! And `signal.pause` only means the calling process sleeps until a signal is received -- no use for the OP's request. See my answer for the correct approach. – Alex Martelli Jul 16 '10 at 06:29
  • @Alex - thanks for the clarification. The signal part of my response was an afterthought.. cheers :) – dpn Jul 16 '10 at 08:05
3

The multiprocessing module works fine on Windows. See the documentation here (end of first paragraph):

http://docs.python.org/library/multiprocessing.html

On the wxPython IRC channel, we had a couple fellows trying multiprocessing out and they said it worked. Unfortunately, I have yet to see anyone who has written up a good example of multiprocessing and wxPython.

If you (or anyone else on here) come up with something, please add it to the wxPython wiki page on threading here: http://wiki.wxpython.org/LongRunningTasks

You might want to check that page out regardless as it has several interesting examples using threads and queues.

Right leg
  • 16,080
  • 7
  • 48
  • 81
Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • Note that multiprocessing is not working on Windows depending on the args. The args is pickled while spawning another process which ends up to error if that args cannot be pickled by Python. – Kazuya Gosho Jul 06 '22 at 15:21
1

You might take a look at the Windows API for thread suspension.

As far as I'm aware there is no POSIX/pthread equivalent. Furthermore, I cannot ascertain if thread handles/IDs are made available from Python. There are also potential issues with Python, as its scheduling is done using the native scheduler, it's unlikely that it is expecting threads to suspend, particularly if threads suspended while holding the GIL, amongst other possibilities.

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
1

I had the same issue. It is more effective to use time.sleep(1800) in the thread loop to pause the thread execution.

e.g

MON, TUE, WED, THU, FRI, SAT, SUN = range(7) #Enumerate days of the week
Thread 1 : 
def run(self):
        while not self.exit:
            try:
                localtime = time.localtime(time.time())
                #Evaluate stock
                if localtime.tm_hour > 16 or localtime.tm_wday > FRI:
                    # do something
                    pass
                else:
                    print('Waiting to evaluate stocks...')
                    time.sleep(1800)
            except:
                print(traceback.format_exc())

Thread 2
def run(self):
    while not self.exit:
        try:
            localtime = time.localtime(time.time())
            if localtime.tm_hour >= 9 and localtime.tm_hour <= 16:
                # do something
                pass
            else:
                print('Waiting to update stocks indicators...')
                time.sleep(1800)
        except:
            print(traceback.format_exc())