Synchronizes access to methods of a class with either an instance or class specific lock.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | """
Defines a decorator, synchronous, that allows calls to methods
of a class to be synchronized using an instance based lock.
tlockname must refer to an instance variable that is some
kind of lock with methods acquire and release. For thread safety this
would normally be a threading.RLock.
"""
from functools import wraps
def synchronous( tlockname ):
"""A decorator to place an instance based lock around a method """
def _synched(func):
@wraps(func)
def _synchronizer(self,*args, **kwargs):
tlock = self.__getattribute__( tlockname)
tlock.acquire()
try:
return func(self, *args, **kwargs)
finally:
tlock.release()
return _synchronizer
return _synched
#-----------------------------------------------------------------
# Usage Examples:
if __name__ == "__main__":
import threading
class MyClass(object):
mm = 0
def __init__(self):
self.mylock = threading.RLock()
self.myList = []
self.n = 0
@synchronous('mylock')
def addToMyList(self, item):
self.n = self.n+1
self.__class__.mm = self.mm+1
time.sleep(0.01)
self.myList.append(str(self.n)+str(self.mm)+item)
self.n = self.n-1
self.__class__.mm = self.mm-1
@synchronous('mylock')
def popListItem(self):
return self.myList.pop(0)
#Within any instance of MyClass, the methods addToMyList and
#popListItem cannot be invoked at the same time (via different threads).
class Class2(object):
classlock = threading.RLock()
mm = 0
def __init__(self):
self.myList = []
self.n = 0
@synchronous('classlock')
def addToMyList(self, item):
self.n = self.n+1
self.__class__.mm = self.mm+1
time.sleep(0.01)
self.myList.append(str(self.n)+str(self.mm)+item)
self.n = self.n-1
self.__class__.mm = self.mm-1
@synchronous('classlock')
def popListItem(self):
return self.myList.pop(0)
# In the above example all instances use the same class based lock.
# The decorator may be used to synchronize methods of an existing class
# by subclassing the class, with the __init__ method creating the lock
# and the methods that are to be synchronized redeclared, as in the following.
#Existing unsynchronized class---
class SomeBase(object):
mm = 0
def __init__(self, arg1):
self.arg1 = arg1
self.myList = []
self.n = 0
def addToMyList(self, item):
self.n = self.n+1
self.__class__.mm = self.mm+1
time.sleep(0.01)
self.myList.append(str(self.n)+str(self.mm)+item)
self.n = self.n-1
self.__class__.mm = self.mm-1
def popListItem(self):
return self.myList.pop(0)
#Derived synchronous class---
class SafeSomeBase(SomeBase):
def __init__(self, arg1):
SomeBase.__init__(self,arg1)
self.instlock = threading.RLock()
addToMyList = synchronous('instlock')(SomeBase.addToMyList)
popListItem = synchronous('instlock')(SomeBase.popListItem)
plock = threading.RLock()
import time
def threadProc( name, baseobj ):
popped=[]
tsleep = .05 + ((ord(name[0])+ord(name[3]))%11)*.01
for j in range(5):
baseobj.addToMyList( name+str(j) )
time.sleep(tsleep)
try:
popped.append(baseobj.popListItem())
except Exception, ex:
print ex
time.sleep(.13);
time.sleep(1.0)
plock.acquire()
print name, popped
plock.release()
def synchTest(thrdnames, synchlist):
threads=[]
ix = 0
for n in thrdnames:
synched = synchlist[ix]
ix = (ix+1) % len(synchlist)
thrd = threading.Thread(target=threadProc, name = n, args=(n, synched))
threads.append( thrd)
for thread in threads:
thread.start()
# for av in range(22):
# lcpy = []
# lcpy.extend(synched.myList)
# print lcpy
# time.sleep(.056)
for thread in threads:
thread.join()
ok = raw_input("Next? ")
thrdnames = "Karl Nora Jean Lena Bart Dawn Owen Dave".split()
synched = [MyClass(), MyClass()]
synchTest(thrdnames, synched)
synched = [Class2(), Class2()]
synchTest(thrdnames, synched)
synched = [SafeSomeBase('alph'),SafeSomeBase('beta')]
synchTest(thrdnames, synched)
synched = [SomeBase('alph'), SomeBase('beta')]
synchTest(thrdnames, synched)
|
This module is very similar to the decorator in ActiveState recipe 465057. The difference being the decorator in that recipe requires a specific lock instance, whereas this one allows the lock to exist as an instance member or a class member, with the attribute name provided to the decorator.
The test examples show the effects of various locking, whether the lock is instance based or class based, or in the last case when no lock is used.
When the addToMyList method it calls it first increments instance and class based variables, waits a bit, and appends a string containing the value of those variables to its list. If instance based locking is present, the first digit should always be 1. If class based locking is present, the first and second digits should always be 1. When no locking is present, the values can change.
The last example shows how to derive a threadsafe subclass from an unprotected base.