[Tutor] Changing instance attributes in different threads

Michael Lange klappnase at freenet.de
Tue Feb 7 23:31:06 CET 2006


On Tue, 07 Feb 2006 06:02:45 -0500
Kent Johnson <kent37 at tds.net> wrote:

> 
> One way to make this code thread-safe is to use a threading.Condition() 
> instead of a boolean variable:
> 
> thread 1 does:
> 
>      self.lock.acquire()
>      if condition:
>          self.name = 'bob'
>      else:
>          self.name = 'mike'
>      self.lock.release()
> 
> thread 2 does:
> 
>      self.lock.acquire()
>      n = self.name
>      self.lock.release()
>      if n == 'bob':
>          <do something>
>      else:
>          <do something else>
> 
> If this is the only communication or synchronization between the two 
> threads I don't think the lock is needed at all - thread 2 is presumably 
> in a loop and thread 1 is controlling the behaviour of the loop 
> asynchronously. If there is some other kind of synchronization between 
> the loops, and thread 2 is only supposed to run once for each setting of 
> self.name in thread 1, you could use Condition.wait() and 
> Condition.notify() to do the synchronization.
> 

Thanks Kent,

In fact I have three threads, a main gui thread and two child threads. Child thread 1 reads data
from the soundcard and appends these data to two lists which I use as recording buffer. The data from
list 1 are used by the gui thread to draw a vumeter, the data from list 2 are written to the
target file. So the communication between the threads occurs when the gui thread resp. child thread 2
read and empty the buffer lists to process the data.
So my functions currently (with boolean "locks") look like:

gui thread (this function is called periodically by Tkinter):

    def get_peaks(self):
        if self.vu_locked:
            return None
        self.vu_locked = 1
        data = [x for x in self.vubuffer]
        self.vubuffer = []
        self.vu_locked = 0
        if not data:
            return None
        left, right = 0, 0
        for d in data:
            left = max(audioop.max(audioop.tomono(d, 2, 1, 0), 2), left)
            right = max(audioop.max(audioop.tomono(d, 2, 0, 1), 2), right)
        return left, right

thread 1:

        vubuffer = []
        recbuffer = []
        while self.running:
            data = self._audioobj.read(self._fragsize)# read data from soundcard
            vubuffer.append(data)
            if not self.vu_locked:
                self.vu_locked = 1
                self.vubuffer += vubuffer
                vubuffer = []
                self.vu_locked = 0
            if self.recording:
                recbuffer.append(data)
                if not self.rec_locked:
                    self.rec_locked = 1
                    self.recbuffer += recbuffer
                    recbuffer = []
                    self.rec_locked = 0

thread 2:

        while self.recording:
            # wait a moment until there is something in the buffer to be written
            time.sleep(0.1)
            if not self.rec_locked:
                self.rec_locked = 1
                data = [x for x in self.recbuffer]
                self.recbuffer = []
                self.rec_locked = 0
                for d in data:
                    self._waveobj.writeframesraw(d)# write the data to a file

So I think I need two Condition objects here; it is most important here that thread 1 does not
block to minimize the risk of recording buffer overruns, but from reading the docs I am
not sure about the correct procedure. So if I change self.rec_locked and self.vu_locked from the
code above to be Condition objects is it correct to do:

thread 1:

        vubuffer = []
        recbuffer = []
        while self.running:
            data = self._audioobj.read(self._fragsize)# read data from soundcard
            vubuffer.append(data)
            if self.vu_locked.acquire(blocking=0):
                self.vubuffer += vubuffer
                vubuffer = []
            self.vu_locked.release()
            if self.recording:
                recbuffer.append(data)
                if self.rec_locked.acquire(blocking=0):
                    self.recbuffer += recbuffer
                    recbuffer = []
                self.rec_locked.release()

thread 2:

        while self.recording:
            # wait a moment until there is something in the buffer to be written
            time.sleep(0.1)
            data = []
            if self.rec_locked.acquire(blocking=0):
                data = [x for x in self.recbuffer]
                self.recbuffer = []
            self.rec_locked.release()
            for d in data:
                self._waveobj.writeframesraw(d)# write the data to a file

Thanks

Michael


More information about the Tutor mailing list