r61100 - python/trunk/Lib/threading.py
Author: jeffrey.yasskin Date: Thu Feb 28 07:09:19 2008 New Revision: 61100 Modified: python/trunk/Lib/threading.py Log: Thread.start() used sleep(0.000001) to make sure it didn't return before the new thread had started. At least on my MacBook Pro, that wound up sleeping for a full 10ms (probably 1 jiffy). By using an Event instead, we can be absolutely certain that the thread has started, and return more quickly (217us). Before: $ ./python.exe -m timeit -s 'from threading import Thread' 't = Thread(); t.start(); t.join()' 100 loops, best of 3: 10.3 msec per loop $ ./python.exe -m timeit -s 'from threading import Thread; t = Thread()' 't.isAlive()' 1000000 loops, best of 3: 0.47 usec per loop After: $ ./python.exe -m timeit -s 'from threading import Thread' 't = Thread(); t.start(); t.join()' 1000 loops, best of 3: 217 usec per loop $ ./python.exe -m timeit -s 'from threading import Thread; t = Thread()' 't.isAlive()' 1000000 loops, best of 3: 0.86 usec per loop To be fair, the 10ms isn't CPU time, and other threads including the spawned one get to run during it. There are also some slightly more complicated ways to get back the .4us in isAlive() if we want. Modified: python/trunk/Lib/threading.py ============================================================================== --- python/trunk/Lib/threading.py (original) +++ python/trunk/Lib/threading.py Thu Feb 28 07:09:19 2008 @@ -404,7 +404,7 @@ self.__args = args self.__kwargs = kwargs self.__daemonic = self._set_daemon() - self.__started = False + self.__started = Event() self.__stopped = False self.__block = Condition(Lock()) self.__initialized = True @@ -419,7 +419,7 @@ def __repr__(self): assert self.__initialized, "Thread.__init__() was not called" status = "initial" - if self.__started: + if self.__started.isSet(): status = "started" if self.__stopped: status = "stopped" @@ -430,7 +430,7 @@ def start(self): if not self.__initialized: raise RuntimeError("thread.__init__() not called") - if self.__started: + if self.__started.isSet(): raise RuntimeError("thread already started") if __debug__: self._note("%s.start(): starting thread", self) @@ -438,8 +438,7 @@ _limbo[self] = self _active_limbo_lock.release() _start_new_thread(self.__bootstrap, ()) - self.__started = True - _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack) + self.__started.wait() def run(self): try: @@ -472,7 +471,7 @@ def __bootstrap_inner(self): try: - self.__started = True + self.__started.set() _active_limbo_lock.acquire() _active[_get_ident()] = self del _limbo[self] @@ -581,7 +580,7 @@ def join(self, timeout=None): if not self.__initialized: raise RuntimeError("Thread.__init__() not called") - if not self.__started: + if not self.__started.isSet(): raise RuntimeError("cannot join thread before it is started") if self is currentThread(): raise RuntimeError("cannot join current thread") @@ -621,7 +620,7 @@ def isAlive(self): assert self.__initialized, "Thread.__init__() not called" - return self.__started and not self.__stopped + return self.__started.isSet() and not self.__stopped def isDaemon(self): assert self.__initialized, "Thread.__init__() not called" @@ -630,7 +629,7 @@ def setDaemon(self, daemonic): if not self.__initialized: raise RuntimeError("Thread.__init__() not called") - if self.__started: + if self.__started.isSet(): raise RuntimeError("cannot set daemon status of active thread"); self.__daemonic = daemonic @@ -672,7 +671,7 @@ def __init__(self): Thread.__init__(self, name="MainThread") - self._Thread__started = True + self._Thread__started.set() _active_limbo_lock.acquire() _active[_get_ident()] = self _active_limbo_lock.release() @@ -718,7 +717,7 @@ # instance is immortal, that's bad, so release this resource. del self._Thread__block - self._Thread__started = True + self._Thread__started.set() _active_limbo_lock.acquire() _active[_get_ident()] = self _active_limbo_lock.release()
jeffrey.yasskin wrote:
To be fair, the 10ms isn't CPU time, and other threads including the spawned one get to run during it. There are also some slightly more complicated ways to get back the .4us in isAlive() if we want.
Would one of those 'slightly more complicated' ways be to keep the old boolean flag around and set it to true before signalling the new event object? Then __repr__ and isAlive could just continue to query the boolean flag, with the event used only in thread.start() Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
participants (3)
-
Christian Heimes
-
jeffrey.yasskin
-
Nick Coghlan