> Create a class which will ensure > turn-taking of events, using a get method with and integer index, by > waiting for the prior index to complete before starting the next.
from thread import start_new_thread as launch from threading import Lock import time from functools import partial class WithObj: def __init__( self, enter, exit ): self.__enter__, self.__exit__= enter, exit class Step: def __init__( self ): self._index= 0 self._locks= {} self._opened= False self._oplock= Lock() def __getitem__( self, index ): with self._oplock: lock= self._locks.get( index, None ) if None is lock: lock= self._locks[ index ]= Lock() if index!= self._index: assert lock.acquire( False ) return WithObj( partial( self.ienter, index ), partial( self.iexit, index ) ) def ienter( self, index ): with self._oplock: if self._opened: return self lock= self._locks.get( index ) assert lock.acquire() with self._oplock: return self def iexit( self, index, *a ): with self._oplock: self._index+= 1 lock= self._locks.get( self._index ) if None is not lock: lock.acquire( False ) lock.release() def complete( self ): with self._oplock: self._index= 0 lock= self._locks.get( 0 ) if None is not lock: lock.acquire( False ) lock.release() def open( self ): with self._oplock: self._opened= True for lock in self._locks.values(): lock.acquire( False ) lock.release() class CustThread: count= 0 def __init__( self ): CustThread.count+= 1 self.id= CustThread.count self.step= Step() self.cont= True self.cmd= None self.ret= None def __repr__( self ): return '<CustThread %i>'% self.id def thloop( thd ): while thd.cont: with thd.step[1]: if not thd.cont: break print( 'step 1', end= ' ' ) thd.ret= thd.cmd+ 1 with thd.step[3]: print( 'step 3' ) thd.ret= None thd.step.complete() def op100( thd ): with thd.step[0]: print( 'step 0', end= ' ' ) thd.cmd= 100 with thd.step[2]: print( 'step 2', end= ' ' ) ret1= thd.ret assert ret1== 101 def main( func ): if __name__== '__main__': func() @main def fmain(): class Case: def __init__( self ): self.th1= CustThread() while 1: print( '===============================' ) class Case1: case= Case() launch( thloop, ( case.th1, ) ) for _ in range( 10 ): case.th1.cmd= None case.th1.ret= None op100( case.th1 ) case.th1.cont= False case.th1.step.open() print( 'case complete' ) while 1: time.sleep( 1 ) ''' Kind of trivial, interesting technique, and general. Enter with thd.step[n]: to indicate step n in a process. with thd.step[m] for m! = n will block until it exits, and step[n+1] gets the go, or step[0] with step.complete(). step.open() grants every step, such as in the case of completion; set termination conditions prior to its call, and check them in any sensitive loops. One way to replace magic sequence numbers with meaning is the "Step1 Step2 Step3" style used in namedtuple, but they're only shown bare here. Sequence numbers are looked up in a hash; special applications can use an array, and account for the possibility that with step[10]: may be called before even step[0]. 0- and 1-based options are customizable. Sequence length may be known at creation time; if so, the Step instance can be initialized with it; contraindicating cases may include with step[n] ... with step[n+2], that don't mean the right thing in a separate instance. __getitem__ returns a WithObj, calling __enter__ and __exit__ on which routes to the Step instance, paired with the specified index. You could probably cache those. The example is a 1-SetOp, 2-DoOp, 1-GetReturn triplet. acquire( False ) / release() pairs release a thread, whether it is acquired already or not. _oplock synchronizes _index and lock lookup operations across an instance. Kind of cool. ''' -- http://mail.python.org/mailman/listinfo/python-list