Threading problems at program exit

Ype Kingma ykingma at accessforall.nl
Sat Nov 30 05:23:24 EST 2002


Dave Cole wrote:

> The following program demonstrates a problem I am experiencing with
> the threading module.  I have an object (A) which holds a lock on
> another object (B).  When object A is deleted I want it to release any
> lock it may still be holding on object B.
> 
> Everything works fine except when the program terminates.
> 
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
> import sys
> import threading
> 
> class Locked:
>     def __init__(self, lock):
>         self._lock = lock
>         self._lock_count = 0
>         self._thread = None
>         self._log = sys.stderr.write
>         self._current_thread = threading.currentThread
>         self.lock()
> 
>     def lock(self):
>         self._log('locked in %s\n' % self._current_thread())
>         self._lock.acquire()
>         self._lock_count += 1
> 
>     def unlock(self):
>         self._log('unlocked in %s\n' % self._current_thread())
>         self._lock_count -= 1
>         self._lock.release()
> 
>     def __del__(self):
>         while self._lock_count:
>             self.unlock()
> 
> obj = Locked(threading.RLock())
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
> 
> When I run this program I get the following:
> 
> locked in <_MainThread(MainThread, started)>
> unlocked in <_DummyThread(Dummy-1, started daemon)>
> Exception exceptions.AssertionError: <exceptions.AssertionError instance
> at 0x814fbfc> in <bound method Locked.__del__ of <__main__.Locked instance
> at 0x816e5f4>> ignored
> 
> It looks like interpreter is deleting thread objects before objects
> which hold locks in those threads.  Is there any kosher way I can
> avoid the problem?
> 
> The only way I can think to fix it is a bit non-kosher:
> 
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
> import sys
> import threading
> 
> class Locked:
>     def __init__(self, lock):
>         self._lock = lock
>         self._lock_count = 0
>         self._thread = None
>         self._log = sys.stderr.write
>         self._current_thread = threading.currentThread
>         self.lock()
> 
>     def lock(self):
>         self._log('locked in %s\n' % self._current_thread())
>         self._lock.acquire()
>         self._lock_count += 1
> 
>     def unlock(self):
>         self._log('unlocked in %s\n' % self._current_thread())
>         self._lock_count -= 1
>         self._lock.release()
> 
>     def __del__(self):
>         if self._lock_count:
>             count, owner = self._lock._release_save()
>             self._log('owner was %s\n' % owner)
>             owner = self._current_thread()
>             self._log('owner is now %s\n' % owner)
>             self._lock._acquire_restore((count, owner))
>             while self._lock_count:
>                 self.unlock()
> 
> obj = Locked(threading.RLock())
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
> 
> This prints the following:
> 
> locked in <_MainThread(MainThread, started)>
> owner was <_MainThread(MainThread, stopped)>
> owner is now <_DummyThread(Dummy-1, started daemon)>
> unlocked in <_DummyThread(Dummy-1, started daemon)>
> 
> The trouble is that it requires the use of private methods of the
> RLock class.
> 
> Is there a better or more kosher way?
> 
> - Dave

You might consider not using the __del__ method at all because 
'it is not guaranteed that __del__() methods are called for objects that 
still exist when the interpreter exits. ' (quoted from the language ref).
Then use:

obj = Locked(threading.RLock())
try:
     # whatever needs to be done with obj
finally:
    obj.unlock()

Regards,
Ype

-- 
email at xs4all.nl



More information about the Python-list mailing list