python: elegant way to deal with lock on a variable?
I have code that looks like something like this:
def startSearching(self):
self.searchingLock.acquire()
searching = self.searching
if self.searching:
self.searchingLock.release()
self.logger.error("Already searching!")
return False
self.searching = True
self.searchingLock.release()
#some more init code, then start the thread which
#constantly checks self.searching to determine when to stop
it's kind of ugly, though. lots of acquires and releases. this looks prettier:
def startSearching(self):
with self.searchingLock:
if self.searching:
self.logger.error("Already searching!")
return False
self.searching = True
#some more init code,开发者_C百科 then start the thread which
#constantly checks self.searching to determine when to stop
but this keeps the lock longer than strictly necessary, espcially if self.logger.error
takes a while (like if it writes to disk, which it does). is there any middle ground between holding the lock as little as possible but having prettier code?
Maybe you need to separate this logic like:
def initSearch(self):
with self.searchingLock:
if self.searching : raise SearchingError('AlreadySearching')
self.searching = True
def startSearching(self):
try: self.initSearch()
except SearchingError as error :
self.logger.error(error.message)
return False
#some more init code, then start the thread which
#constantly checks self.searching to determine when to stop
And additionaly you telling your searchingLock
the reason to release it automaticaly.
How about wrapping the variable & lock in a class:
class LockedVariable(object):
def __init__(self, value, lock=None):
self._value = value
self._lock = lock if lock else threading.RLock()
self._locked = false:
@property
def locked(self):
return self._locked
def assign(self, value):
with self:
self._value = value
def release():
self._locked = False
return self._lock.release()
def __enter__(self):
self._lock.__enter__()
self._locked = True
return self._value
def __exit__(self, *args, **kwargs):
if self._locked:
self._locked = False
return self._lock.__exit__(*args, **kwargs)
And use as this:
locked_dict = LockedVariable({})
with locked_dict as value:
value['answer'] = 42
if locked_dict.locked:
locked_dict.release()
print 'eureka! :)'
return
if locked_dict.locked:
print 'bahh! :('
Comment:
I sometimes use boost::shared_ptr with a custom deleter to achieve the same thing, i.e. return an unlocked variable that's released when it goes out of scope.
This will save you one "self.searchingLock.release()
" Guess it's not very pythonic or anything but it does the job
def startSearching(self):
self.searchingLock.acquire()
already_searching = self.searching
self.searching = True # Since it'll be true in both scenarios
self.searchingLock.release()
if already_searching:
self.logger.error("Already searching!")
return not already_searching
精彩评论