address higher-level synchronization issues at all. For example, as we saw, when more
than one thread might attempt to update the same variable at the same time, the threads
should generally be given exclusive access to the object with locks. Otherwise, it’s not
impossible that thread switches will occur in the middle of an update statement’s
bytecode.
Locks are not strictly required for all shared object access, especially if a single thread
updates an object inspected by other threads. As a rule of thumb, though, you should
generally use locks to synchronize threads whenever update rendezvous are possible
instead of relying on artifacts of the current thread implementation.
The thread switch interval
Some concurrent updates might work without locks if the thread-switch interval is set
high enough to allow each thread to finish without being swapped out. The
sys.setcheckinterval(N) call sets the frequency with which the interpreter checks for
things like thread switches and signal handlers.
This interval defines the number of bytecode instructions before a switch. It does not
need to be reset for most programs, but it can be used to tune thread performance.
Setting higher values means switches happen less often: threads incur less overhead but
they are less responsive to events. Setting lower values makes threads more responsive
to events but increases thread switch overhead.
Atomic operations
Because of the way Python uses the GIL to synchronize threads’ access to the virtual
machine, whole statements are not generally thread-safe, but each bytecode instruction
is. Because of this bytecode indivisibility, some Python language operations are thread-
safe—also called atomic, because they run without interruption—and do not require
the use of locks or queues to avoid concurrent update issues. For instance, as of this
writing, list.append, fetches and some assignments for variables, list items, dictionary
keys, and object attributes, and other operations were still atomic in standard C Python;
others, such as x = x+1 (and any operation in general that reads data, modifies it, and
writes it back) were not.
As mentioned earlier, though, relying on these rules is a bit of a gamble, because they
require a deep understanding of Python internals and may vary per release. Indeed, the
set of atomic operations may be radically changed if a new free-threaded implementa-
tion ever appears. As a rule of thumb, it may be easier to use locks for all access to global
and shared objects than to try to remember which types of access may or may not be
safe across multiple threads.
C API thread considerations
Finally, if you plan to mix Python with C, also see the thread interfaces described in
the Python/C API standard manual. In threaded programs, C extensions must release
212 | Chapter 5: Parallel System Tools