1

Is there a way in python to stop a thread? I have a gui with a method which plays 10 second audio files and sends information to GUI window continuously

I am Multithreading because I dont want the GUI to freeze while my files play. I can stop the thread with my current code but takes a while

My code looks something like this :

class MyThread(threading.Thread):
   """Thread class with a stop() method. The thread itself has to check
    regularly for the stopped() condition."""

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

    def stop(self):
        self._stop.set()

    def stopped(self):
        return self._stop.isSet()


 class Myplayer: 

   // GUI CODE


    def play_button(self, widget):
        self.mythread = MyThread(target=self.practice)
        self.mythread.start()

    def stop_button(self, widget):
         if self.mythead.IsAlive:
            self.self.stop()



    def mplayer(self):
        while not self.mythread.stopped:
        gobject.idle_add(self.insert_text, "\nPlaying a new file")
        subprocess.call(["timidity", "myfile.mid"])
Ada Xu
  • 953
  • 4
  • 14
  • 31
  • I believe that only a thread can stop itself. You can, however, set a global variable for hat thread to continuously check, which will tell it to stop. – TheSoundDefense Jul 19 '14 at 21:14
  • What GUI system are you using? Python threads are limited by the [Global Interpreter Lock](http://en.wikipedia.org/wiki/Global_Interpreter_Lock) meaning only one thread is ever actually running at a time. If your GUI thread is maintained inside the main Python thread, then it's likely going to freeze while another python thread has access to the GIL. – aruisdante Jul 19 '14 at 21:15
  • 1
    *"I can stop the thread with my current code but takes a while."* It's probably taking a while because you're doing something blocking in your `while` loop, which means you wait for whatever blocking operation is running to finish before checking the `stopped` property. Can you share what's going on in the while loop? – dano Jul 19 '14 at 21:16
  • @dano Indeed. As I warned the OP in their other [question](http://stackoverflow.com/questions/24843193/stopping-a-python-thread-running-an-infinite-loop/24843260#24843260), the thread only stops at the start of a new loop. – aruisdante Jul 19 '14 at 21:17
  • @dano Yes. I am using a `subprocess.call` to a midi player and that's the blocking operation. However, I can't use `Popen` either. – Ada Xu Jul 19 '14 at 21:21
  • @AdaXu Is that `while` loop code you're showing actually occuring in `MyThread.run`? – dano Jul 19 '14 at 21:25

2 Answers2

2

Assuming you want to interrupt your midi file while it's playing if your thread is stopped, you can stop the thread more quickly by using Popen instead of call, and then waiting in a loop for either the process to finish, or for the stop request to come in:

class MyThread(threading.Thread):
   """Thread class with a stop() method. The thread itself has to check
    regularly for the stopped() condition."""

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

    def stop(self):
        self._stop.set()

    def stopped(self):
        return self._stop.isSet()

    def run(self):
        while not self.stopped:
           gobject.idle_add(self.insert_text, "\nPlaying a new file")
           p = subprocess.Popen(["timidity", "myfile.mid"])
           while p.poll() is None: # This will loop until the process has exited.
               if self.stopped:
                   # Someone set the stop flag. Kill the process and exit
                   p.terminate()
                   p.join()
                   return
               time.sleep(.1) # Sleep briefly so we don't hog CPU cycles
dano
  • 91,354
  • 19
  • 222
  • 219
  • Wow That wasn't too complicated. Thanks a lot – Ada Xu Jul 19 '14 at 21:56
  • In fact, using ``pOpen`` actually removes the need for a ``thread`` in the first place (if calling ``pOpen`` is all it's doing). It's opening a separate child process. You can simply store the returned value somewhere and do the exact same thing directly from the GUI thread to kill it. – aruisdante Jul 20 '14 at 04:51
0

Here's an example. We start a single thread which does all the work. After two seconds, we tell it to die by setting the shared Event flag.

The worker thread generally runs in a loop, doing a little processing, then checking the flag. If it's set, then it exits, otherwise the thread does some more work.

source

import time
from threading import *

class WorkerThread(Thread):
    def __init__(self, die_flag, *args, **kw):
        super(WorkerThread,self).__init__(*args, **kw)
        self.die_flag = die_flag

    def run(self):
        for num in range(3):
            if self.die_flag.is_set():
                print "{}: bye".format(
                    current_thread().name
                    )
                return
            print "{}: num={}".format(
                current_thread().name, num,
                )
            time.sleep(1)

flag = Event()

WorkerThread(name='whiskey', die_flag=flag).start()
time.sleep(2)

print '\nTELL WORKERS TO DIE'
flag.set()

print '\nWAITING FOR WORKERS'
for thread in enumerate():
    if thread != current_thread():
        print thread.name,
        thread.join()
    print

output

whiskey: num=0
whiskey: num=1

TELL WORKERS TO DIE

WAITING FOR WORKERS

whiskey whiskey: bye
johntellsall
  • 14,394
  • 4
  • 46
  • 40