Einfach alle Methoden-Aufrufe durch einen einzigen, gemeinsamen Mutex zu tunneln, skaliert meistens nicht besonders und führt oft zu einer Ent-Parallelisierung, weil ja alle Aufrufe dadurch hintereinander passieren. Es ist auch meistens besser, einen komplexeren 
Monitor statt bloßer Locks zu verwenden, und zwar pro Menge von Attributen, deren Zustand einer gemeinsamen Invariante unterliegt. Diese Invariante ist nämlich idR. komplizierter, als bloß 
"dieses Attribut darf nicht gleichzeitig gelesen und geschrieben werden", wie es durch ein Lock bzw. RLock ermöglicht würde. Einen Ringpuffer etwa, in den von einem Thread geschrieben und aus dem aus einem anderen Thread gelesen wird, kann man damit nicht bauen.
Threadsicherheit bei einem Ringpuffer bedeutet: wenn der Puffer 
leer ist, muss gewartet werden, bis etwas hineingeschrieben wurde, erst dann kann man wieder lesen. Wenn er 
voll ist, muss gewartet werden, bis etwas gelesen wurde, erst dann kann man wieder hineinschreiben. Dazu verwendet man eine Liste fester Größe und zwei Zeiger, 
links und 
rechts, die sich in Einer-Schritten in dieselbe Rchtung bewegen (wobei 
rechts vorneweg marschieren darf), die sich aber gegenseitig nie überholen dürfen. Wenn 
links == rechts ist, ist der Puffer 
leer, wenn 
links == (rechts + 1) % Größe des Puffers ist, ist er 
voll. Damit in den jeweiligen Zuständen 
leer und 
voll gewartet wird, benötigt man einen 
Monitor mit 
wait sets, der über ein Condition Objekt kontrolliert wird, zB. so:
Code: Alles auswählen
from threading import Thread, Condition
class Ringbuffer(object):
    def __init__(self, size):
        assert size > 0
        self._size = size
        self._items = [None] * size
        self._left = self._right = 0
        self._cond = Condition()
    def is_full(self):
        with self._cond:
            return self._left == (self._right + 1) % self._size
    def is_empty(self):
        with self._cond:
            return self._left == self._right
    def put(self, item):
        with self._cond:
            while self.is_full():
                self._cond.wait()
            self._right += 1
            self._right %= self._size
            self._items[self._right] = item
            print 'put', item
            self._cond.notify()
    def get(self):
        with self._cond:
            while self.is_empty():
                self._cond.wait()
            self._left += 1
            self._left %= self._size
            item = self._items[self._left]
            print 'get', item
            self._cond.notify()
            return item
SENTINEL = object()
def retrieve(buf):
    while True:
        item = buf.get()
        if item is SENTINEL:
            print 'Goodbye!'
            break
some_buffer = Ringbuffer(5)
some_buffer.put(1)
some_buffer.put(2)
some_buffer.put(3)
t = Thread(target=lambda:retrieve(some_buffer))
t.start()
some_buffer.put('joe')
some_buffer.put(5)
some_buffer.put(6)
some_buffer.put(7)
some_buffer.put('jim')
some_buffer.put(SENTINEL)
Ein 
Monitor ist, wie man sieht, nicht ein Objekt oder eine Funktion, es ist überhaupt 
kein Ding, sondern ein abstraktes Konzept, das durch Conditions implementiert wird. Es ist ein 
Design Pattern.
Man kann übrigens, wie DasIch vorgeschlagen hat, auch Dekoratoren verwenden:
Code: Alles auswählen
def locked_with(lock):
    def decorated(method):
        def decorator(self, *args, **kwargs):
            with lock(self):
                return method(self, *args, **kwargs)
        return decorator
    return decorated
def wait_if(is_in_state, get_cond):
    def decorated(method):
        def decorator(self, *args, **kwargs):
            cond = get_cond(self)
            with cond:
                while is_in_state(self):
                    cond.wait()
                result = method(self, *args, **kwargs)
                cond.notify()
                return result
        return decorator
    return decorated
class Ringbuffer(object):
    def __init__(self, size):
        assert size > 0
        self._size = size
        self._items = [None] * size
        self._left = self._right = 0
        self._cond = Condition()
    def cond(self):
        return self._cond
    @locked_with(cond)
    def is_full(self):
        return self._left == (self._right + 1) % self._size
    @locked_with(cond)
    def is_empty(self):
        return self._left == self._right
    @wait_if(is_full, cond)
    def put(self, item):
        self._right += 1
        self._right %= self._size
        self._items[self._right] = item
        print 'put', item
    @wait_if(is_empty, cond)
    def get(self):
        self._left += 1
        self._left %= self._size
        item = self._items[self._left]
        print 'get', item
        return item
get_cond in wait_if() ist leider notwendig, damit man pro Klasse mehrere Conditions haben kann und nicht auf den Namen _cond festgelegt ist.
Wie Monitore funktionieren, und vieles mehr, steht in 
diesem Buch.
Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.