Counting Threads

D

David Poundall

I have a thread class and I want to be able to track its usage within
an application. FYI the class launches aplications in their own thread
when the 'launch' method is called.

That works OK

However I want to take things a bit further and my thinking was that I
could use a class parameter to do a count but I am having no luck I am
afraid.

Its 'GlobalThreadCount' I am trying to get to work. I may need to use
the globals built in - or do I.

Thanks in advance

David

#--------------------------------------------------------

from threading import Thread, Event

class ServerThreads:
"""
Wrapper for thread handling.
Thread ID 0 = Controller
Thread ID 1 = Plant Object 1
Thread ID 2 = Plant Object 2
"""

GlobalThreadCount = 0

def __init__(self):
self.Threads = {}

def launch(self, SubToLaunch, SubsArgs=(),
SubsKwargs={}, AsDeamon=True):
t = Thread(target=SubToLaunch, args = SubsArgs,
kwargs = SubsKwargs)
t.setDaemon(AsDeamon)
t.start()
self.Threads[len(self.Threads)] = t
# Stash the thread in an instance local

GlobalThreadCount += 1

def globalusagecount(self):
"""
Returns a count figure for how many
threads are running in total
using this class.
"""
return GlobalThreadCount


def stop(self,ThreadID):
t = self.Threads[ThreadID]
t.stop()
self.Threads.clear[ThreadID]

def count(self):
"""
Returns alist of how many threads are running
in the instance
"""
return len(self.Threads)

def runningkeys(self):
"""
Returns a llist of all running Keys
"""
return self.Threads.keys

def allOK(self):
"""
Returns a list of all threads that are down if any)
"""
ThreadsDown = []
for t in self.Threads:
if not self.Threads[t].isAlive:
ThreadsDown.append(t)
return ThreadsDown
 
D

David Poundall

Just sorted (its always the way when you post isn't it)

But the count isn't doint what I expected. I was after a increment
after every time the class was used ireespective of instance. Instead
I get a count based on the instance usage.

Idea's anyone?

#
------------------------------------------------------------------------------------
class ServerThreads:
"""
Wrapper for thread handling.
Thread ID 0 = Controller
Thread ID 1 = Plant Object 1
Thread ID 2 = Plant Object 2
"""

GlobalThreadCount = 0

def __init__(self):
global GlobalThreadCount
self.Threads = {}
GlobalThreadCount = 0


def launch(self, SubToLaunch, SubsArgs=(), SubsKwargs={},
AsDeamon=True):
t = Thread(target=SubToLaunch, args = SubsArgs, kwargs =
SubsKwargs)
t.setDaemon(AsDeamon)
t.start()
self.Threads[len(self.Threads)] = t # Stash the
thread in an instance local

global GlobalThreadCount
GlobalThreadCount += 1
print GlobalThreadCount

def globalusagecount(self):
"""
Returns a count figure for how many threads are running in
total
usnig this class.
"""
global GlobalThreadCount
return GlobalThreadCount


def stop(self,ThreadID):
t = self.Threads[ThreadID]
t.stop()
self.Threads.clear[ThreadID]

def count(self):
"""
Returns alist of how many threads are running in the instance
"""
return len(self.Threads)

def runningkeys(self):
"""
Returns a llist of all running Keys
"""
return self.Threads.keys

def allOK(self):
"""
Returns a list of all threads that are down if any)
"""
ThreadsDown = []
for t in self.Threads:
if not self.Threads[t].isAlive:
ThreadsDown.append(t)
return ThreadsDown
 
D

David Poundall

Just sorted (its always the way when you post isn't it)

But the count isn't doint what I expected. I was after a increment
after every time the class was used ireespective of instance. Instead
I get a count based on the instance usage.

Idea's anyone?

#
------------------------------------------------------------------------------------
class ServerThreads:
"""
Wrapper for thread handling.
Thread ID 0 = Controller
Thread ID 1 = Plant Object 1
Thread ID 2 = Plant Object 2
"""

GlobalThreadCount = 0

def __init__(self):
global GlobalThreadCount
self.Threads = {}
GlobalThreadCount = 0


def launch(self, SubToLaunch, SubsArgs=(), SubsKwargs={},
AsDeamon=True):
t = Thread(target=SubToLaunch, args = SubsArgs, kwargs =
SubsKwargs)
t.setDaemon(AsDeamon)
t.start()
self.Threads[len(self.Threads)] = t # Stash the
thread in an instance local

global GlobalThreadCount
GlobalThreadCount += 1
print GlobalThreadCount

def globalusagecount(self):
"""
Returns a count figure for how many threads are running in
total
usnig this class.
"""
global GlobalThreadCount
return GlobalThreadCount


def stop(self,ThreadID):
t = self.Threads[ThreadID]
t.stop()
self.Threads.clear[ThreadID]

def count(self):
"""
Returns alist of how many threads are running in the instance
"""
return len(self.Threads)

def runningkeys(self):
"""
Returns a llist of all running Keys
"""
return self.Threads.keys

def allOK(self):
"""
Returns a list of all threads that are down if any)
"""
ThreadsDown = []
for t in self.Threads:
if not self.Threads[t].isAlive:
ThreadsDown.append(t)
return ThreadsDown
 
P

Peter Hansen

David said:
Just sorted (its always the way when you post isn't it)

But the count isn't doint what I expected. I was after a increment
after every time the class was used ireespective of instance.

I have no idea what that last sentence above means...
Instead
I get a count based on the instance usage.
Idea's anyone?

Several, interspersed below:
------------------------------------------------------------------------------------
class ServerThreads:
"""
Wrapper for thread handling.
Thread ID 0 = Controller
Thread ID 1 = Plant Object 1
Thread ID 2 = Plant Object 2
"""

GlobalThreadCount = 0

Style note: using MixedCaps for variable names doesn't fit conventional
Python style. To be conventional you would use "globalThreadCount"
here, or just "threadCount" since it's no more "global" than any other
class attribute.
def __init__(self):
global GlobalThreadCount

And here you definitely don't want "global" since your GlobalThreadCount
above is *not* a Python "global". It's a class attribute, and you
should reference it as such:
self.Threads = {}
GlobalThreadCount = 0
Change to:
ServerThreads.GlobalThreadCount = 0

and access it as such in all other places inside the class.
def launch(self, SubToLaunch, SubsArgs=(), SubsKwargs={},
AsDeamon=True):
t = Thread(target=SubToLaunch, args = SubsArgs, kwargs =
SubsKwargs)
t.setDaemon(AsDeamon)

You appear to know there is a difference in spelling between these two
"demons"... but only "daemon" is correct. "Deamon" is a misspelling.
t.start()
self.Threads[len(self.Threads)] = t # Stash the
thread in an instance local

global GlobalThreadCount
GlobalThreadCount += 1
print GlobalThreadCount

Caution: if you can't guarantee that launch() will be called from only
one thread, you have a race condition here since the count could be
increased "simultaneously" from multiple threads and you won't get
reliable results. You would want to wrap it with a threading.Lock() to
protect it in that case.
def globalusagecount(self):
"""
Returns a count figure for how many threads are running in
total
usnig this class.
"""
global GlobalThreadCount
return GlobalThreadCount

Style/usage note: although as I mentioned you don't want to use "global"
variables anyway, even if you did the above line is unnecessary. You
only need to use "global" if you are going to *rebind* the global name
(i.e. assign a new value to it), not if you are merely reading the value
and returning it, as above. What you wrote would work, it's just not
needed or usual. (But, again, using a global at all is wrong anyway.)


-Peter
 
D

David Poundall

But the count isn't doint what I expected. I was after a increment
I have no idea what that last sentence above means...

On re-reading it - neither do I. Sorry about that. What I meant to
say was that I was after an increment in the count variable (the class
attribute) not an increment only within the class instances. Because
the variable was being set to zero every time the class was
instantiated I wasn't getting the true usage count.
ServerThreads.GlobalThreadCount = 0

That was what the syntax I was missing.
You appear to know there is a difference in spelling between these two
"demons"... but only "daemon" is correct. "Deamon" is a misspelling.

That one certainly slipped under the radar.
You would want to wrap it with a threading.Lock() to
protect it in that case.

Will do.
But, again, using a global at all is wrong anyway

I posted using this sytax as it was nearly working - but I knew there
was a serious clanger in the code and you pointed it out when you
identified it was the class attribute syntax I was after.

Peter, thank you for taking the time to unpick my code. It's much
apreciated. I have only been coding with Python for the past couple of
weeks and switching my brain to work with Python classes has been a bit
of a challenge. I wil re-read the document section on style tips
before I get too far into my app, as I would really like to be able to
code in a Pythonesque fashion.

David.
 
D

David Poundall

First time I have used thread locking. This seems to work but is it
correct?

from threading import Thread, Event, Lock
..
..
..
def launch(self, ThreadTitle, SubToLaunch, SubsArgs=(),
SubsKwargs={}, AsDaemon=True):
my_lock = Lock()
my_lock.acquire()
try:
# ---------------------
t = Thread(target=SubToLaunch, args = SubsArgs, kwargs =
SubsKwargs)
t.setDaemon(AsDaemon)
t.start()
self.Threads[len(self.Threads)] = t # Stash the
thread in an instance local
# ---------------------
ServerThreads.threadcount += 1
finally:
my_lock.release()
..
..
..
 
D

Dennis Lee Bieber

First time I have used thread locking. This seems to work but is it
correct?
Not quite, since you may also want to lock it around the print
option too...

Create the lock just like you did the counter (CLASS level), then
reference it using the class name prefix in all places (you should only
need the acquire/release pair in those places, but put them around all
uses of the counter).
--
 
D

David Poundall

Sorry Denis - but could you give me an example. I can't for the life
of me see how the syntax for that would go.
 
D

Dennis Lee Bieber

Sorry Denis - but could you give me an example. I can't for the life
of me see how the syntax for that would go.

I'm having to recreate your code from snippets posted over the past
few messages...

#
------------------------------------------------------------------------------------
class ServerThreads:
"""
Wrapper for thread handling.
Thread ID 0 = Controller
Thread ID 1 = Plant Object 1
Thread ID 2 = Plant Object 2
"""

GlobalThreadCount = 0
CounterLock = threading.Lock()

def __init__(self):
self.Threads = {}
# GlobalThreadCount = 0 ## unneeded here


def launch(self, SubToLaunch, SubsArgs=(), SubsKwargs={},
AsDeamon=True):
t = Thread(target=SubToLaunch, args = SubsArgs, kwargs =
SubsKwargs)
t.setDaemon(AsDeamon)
t.start()
self.Threads[len(self.Threads)] = t # Stash the
thread in an instance local

# global GlobalThreadCount ## not a global
ServerThreads.CounterLock.acquire()
ServerThreads.GlobalThreadCount += 1
print ServerThreads.GlobalThreadCount
ServerThreads.CounterLock.release()

def globalusagecount(self):
"""
Returns a count figure for how many threads are running in
total
usnig this class.
"""
# global GlobalThreadCount ##not global
ServerThreads.CounterLock.acquire()
tmp = ServerThreads.GlobalThreadCount
ServerThreads.CounterLock.release()
return tmp ##GlobalThreadCount


def stop(self,ThreadID):
t = self.Threads[ThreadID]
t.stop()
self.Threads.clear[ThreadID]

def count(self):
"""
Returns alist of how many threads are running in the instance
"""
return len(self.Threads)

def runningkeys(self):
"""
Returns a llist of all running Keys
"""
return self.Threads.keys

def allOK(self):
"""
Returns a list of all threads that are down if any)
"""
ThreadsDown = []
for t in self.Threads:
if not self.Threads[t].isAlive:
ThreadsDown.append(t)
return ThreadsDown



Hmmmm, I notice you never decrement the counter...
--
 
D

David Poundall

After much optimisation it turns out the following code does the job
for me. In the end using count didn't give me the flexibility I
needed. Instead I now name each thread and track them accordingly.
It's arguable if I need the thread locking now though, however I have
left it in to remind me of the syntax.

Thank you for posting back Dennis. Much appreciated.

# --------------------------------------------------------
class ServerThreads:
"""
Wrapper for thread handling. These threads are not
dynamic, that is to say once the application is fully
loaded the number of threads running is determined
by the size of the application - and is fixed.
"""
# --------------------- Class Attributes
# Dict holds object references to all threads
# created in the server (running or not)
thr_objects = {}
# Dict holds status of all running threads
# 0 = stopped, 1 = running
thr_running = {}
# Lock object
lck = Lock()

def launch(self, ThrName, SubToLaunch,
SubsArgs=(), SubsKwargs={}, AsDaemon=True):

""" Kickoff a thread.
thr_objects : Dictionary using ThreadTitle
as the key which holds
references to the thread object
thr_running : Dictionary holding the status
of each thread
"""
s = ServerThreads
# ---------------------
s.lck.acquire()
try:
t = Thread(name = ThrName, target=SubToLaunch,
args = SubsArgs, kwargs = SubsKwargs)
t.setDaemon(AsDaemon) # Must be set before start
s.thr_objects[ThrName] = t
s.thr_running[ThrName] = 1
if ag.gb_Appdebug: print 'Thread Started ------------- ',
ThrName
t.start()
finally:
s.lck.release()
# ---------------------

def stoprequest(self,thr_name):
""" Thread stop request - stop is pending.
Join is not needed because the main code body
drops out of the thread loops once the
thr_running = true condition has been removed."""
s = ServerThreads
# ---------------------
s.lck.acquire()
s.thr_running[thr_name] = 0 # Flag to tell running
thread to please terminate
if ag.gb_Appdebug: print 'Thread Stopping ----------- ' +
thr_name
s.lck.release()
# ---------------------

def allOK(self):
""" Returns a list of all threads that are down
when they shouldn't be (if any) """
s = ServerThreads
ThreadsDown = []
for thr_name in s.thr_objects:
if not s.thr_objects[thr_name].isAlive() and
s.thr_running[thr_name]:
# If a thread has an unscheduled stop indicate this by
returning the
# threads name otherwise return None.
ThreadsDown.append(thr_name)
return ThreadsDown
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top