Ankur Agarwal
Ankur Agarwal

Reputation: 24748

Number of active threads

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

Answers (1)

John
John

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).

Py_Exit's documentation.

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

Related Questions