Welcome, guest | Sign In | My Account | Store | Cart

If you're familiar with thread programming, Python is reasonably straightforward to work with, once you realize the global interpreter lock is around; however, sometimes they can be trouble. One case is when threads don't die when you expect them to. Resorting to infrequent polling, even if more advanced logic is active in your program, is one way to deal with this, especially during debugging.

Python, 47 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import threading

class myWorker:
	
	def _threadProc( self):
		"""This is where the work gets done"""
		while 1:
			if self._threadShouldStop():
				break 
			"""
			If you're waiting on an event in doSomeWorkOrWait that is not expected
			  to return with no activity, then set a largish (~1 second) timeout.
			 If you're using Python 2.2x, you could use generators and yield out
			   every so often.
			"""
			obj.doSomeWorkOrWait( TIMEOUT)
			if obj.workIsFinished()
				break
			 
	def watchdog( self):
		"""
		If used, this fn should be called "often" - if not, it could be a sign that
		  the calling code has stopped operating as expected.
		  Some time.time() logic could be used in _threadShouldStop 
		   to extend the amount of time available. 
		"""
		self._pingCount += 1
	def _threadShouldStop( self):
		""" Return true if there's a reason the thread should stop"""
		manualStop = self.stopThread 
		mainThreadGone = not self.callingThread.isAlive() 
		somethingStalled = self._pingCount == 0
		self._pingCount = 0

		return manualStop or mainThreadGone or somethingStalled
		
	def startNewThread( self):
		"""
		Start a new thread, executing the code in _threadProc.
		We're assuming the main thread, or whichever we want 
		   to depend on being alive, is calling this function.
		"""
		self.callingThread = threading.currentThread()
		self.stopThread = 0
		self.thread = threading.Thread( target=self._threadProc)
		self.thread.setDaemon( 0)
		self.thread.start()

The code shows a shell of a class that can spawn new threads. I chose to create a thread by explicity passing a reference to _threadProc when creating the thread (which I like better), but you could also subclass threading.Thread and define a default run() method.

_threadShouldStop will stop the thread if any of these conditions are met: - Someone (on the internal or an external thread) sets .stopThread (Provide an external stop() method if you wish) - The watchdog() function is never called between calls to _tSS. As noted, you may want to increase the amount of time the process has to call watchdog() by using time.time(). If your thread depends on some long-running process that may not produce for periods of time but is expected to be running, you may wish to use this method. - The thread that called startNewThread is no longer alive. In most cases, this means that the main thread has exited, and you should probably exit as well.

Obviously if your application doesn't fit some of the conditions for these methods, don't use one or more of the tests.

You may wish to employ one of these methods in addition to event-based thread mangement (a) during debugging (b) in case of thread deadlock conditions that did not occur during testing. In the latter case, at least the application will exit so that it can be restarted sucessfully (manually or automatically), rather than consuming or locking external resources. (c) When components you are using do not co-operate. Often many components (such as GUI toolkits or certain COM objects under win32) will insist on running on the main thread. In this case hoop-jumping is often necessary to get everything working together. (I still haven't figured it all out.)

Created by Michael Robin on Wed, 17 Oct 2001 (PSF)
Python recipes (4591)
Michael Robin's recipes (3)

Required Modules

  • (none specified)

Other Information and Tasks