Reputation: 24748
Python version: '2.7.3 (default, Apr 10 2013, 06:20:15) \n[GCC 4.6.3]'
I have this:
#!/usr/bin/env python
import time, threading, os
def f1(arg1):
for i in xrange(arg1):
time.sleep(1)
print "i is: ", i
print threading.active_count()
print threading.enumerate()
if __name__ == '__main__':
t = threading.Thread(name="MyThread1", target=f1, args=(5,))
t.start()
My question is, why is the number of active threads being reported as 2 and why does the list generated by enumerate
contain the main thread as well.
I was thinking that the main thread terminates after spawning "MyThread1".
$ ./threadeg.py
i is: 0
2
[<_MainThread(MainThread, stopped 139858183157504)>, <Thread(MyThread1, started 139858153768704)>]
i is: 1
2
[<_MainThread(MainThread, stopped 139858183157504)>, <Thread(MyThread1, started 139858153768704)>]
i is: 2
2
[<_MainThread(MainThread, stopped 139858183157504)>, <Thread(MyThread1, started 139858153768704)>]
i is: 3
2
[<_MainThread(MainThread, stopped 139858183157504)>, <Thread(MyThread1, started 139858153768704)>]
i is: 4
2
[<_MainThread(MainThread, stopped 139858183157504)>, <Thread(MyThread1, started 139858153768704)>]
Upvotes: 3
Views: 6567
Reputation: 13699
threading.activeCount() which returns the number of threads active (that were started with that module). [Source]
Since the threading module is a pure python module built on top of the thread module, it's pretty easy to view the source code.
Here is the source code for active_count
def activeCount():
"""Return the number of Thread objects currently alive.
The returned count is equal to the length of the list returned by
enumerate().
"""
with _active_limbo_lock:
return len(_active) + len(_limbo)
upon further investigation it should be noted that there is a _MainThread instance is stored in _active (_active and _limbo are both dictionaries that map thread names to their instances). And is deleted from _active when _exitfunc
is called.
Here is the source for _MainThread,
class _MainThread(Thread):
def __init__(self):
Thread.__init__(self, name="MainThread")
self._Thread__started.set()
self._set_ident()
with _active_limbo_lock:
_active[_get_ident()] = self
def _set_daemon(self):
return False
def _exitfunc(self):
self._Thread__stop()
t = _pickSomeNonDaemonThread()
if t:
if __debug__:
self._note("%s: waiting for other threads", self)
while t:
t.join()
t = _pickSomeNonDaemonThread()
if __debug__:
self._note("%s: exiting", self)
self._Thread__delete()
once _exitfunc
is called _MainThread
waits for all non daemon threads to join and then calls Thread._delete
which in this case has been mangled to __Thread_delete
which in turn removes _MainThread
from the _active
dictionary.
_exitfunc
is assigned to _shutdown
on line 1201.
_shutdown = _MainThread()._exitfunc
_shutdown
is called from pythonrun.c, which in turn is called by Py_Finalize
. Py_Finalize
is called by Py_Exit, which exits the main process (at this point only daemons are left).
Exit the current process. This calls Py_Finalize() and then calls the standard C library function exit(status).
Here is an example to get you the behavior you expect.
import threading, time
def f():
time.sleep(1) #wait for the interpreter to "shutdown"
print threading.enumerate()
if __name__ == '__main__':
t = threading.Thread(target=f)
t.daemon = True
t.start()
threading._shutdown() #simulate an interpreter shutdown
Another great answer describing how threads are shutdown.
Upvotes: 1