For simple tasks, locks and queues are a cumbersome way to prevent race conditions. Here is a simple technique for temporarily keeping control of the intrepreter to execute a few steps atomically.
| Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import sys
### main body of code
### running in a single
### thread goes here
try:
sys.setcheckinterval(sys.maxint)
# statements in this block are
# assured to run atomically
finally:
sys.setcheckinterval(100)
### rest of code in the thread
### goes here
|
Discussion
Sometimes I want to debug or monitor multi-threaded code by inserting a few print statements. Unfortunately, a print generates two opcodes, PRINT_ITEM and PRINT_NEWLINE, which are not guaranteed to execute together (another thread may get control after the first opcode and then do its own print). The current RightWay(tm) is to create a separate daemon thread in charge of all printing and to send print requests to it via the Queue module. But, that is simply too much work compared to:
try:
sys.setcheckinterval(sys.maxint)
print somevalue
finally:
sys.setcheckinterval(100)
The technique works for many tasks such as swapping two global variables using "a,b=b,a" or functions like "pickle.dumps(mydict)" which are non-atomic and could potentially be interrupted in mid-stream.
Use some caution with the technique. It also suspends the interpreter's control-break checking, so you won't be able to break out of the critical section. Also, it's a good idea to do this only with short-running blocks of code. If you keep control too long, the responsiveness of the application will be impacted (because the other threads aren't getting a chance to run).
In Py2.5, the whole pattern can be collapsed to a single with-statement:
with other_threads_suspended():
print somevalue
The with-statements also work well with locks.


Sign in to comment