ActiveState Code

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