Recipe 475125: Atomically execute a block of Python statements
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
importsys### main body of code### running in a single### thread goes heretry:sys.setcheckinterval(sys.maxint)# statements in this block are# assured to run atomicallyfinally: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:
<pre>
try:
sys.setcheckinterval(sys.maxint)
print somevalue
finally:
sys.setcheckinterval(100)
</pre>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:
<pre>
with other_threads_suspended():
print somevalue
</pre> The with-statements also work well with locks.
Sign in to comment