Mutable global state and threads

Kev Dwyer kevin.p.dwyer at gmail.com
Tue Jan 3 13:41:40 EST 2017


Hello List,

I came across some threading code in Some Other place recently and wanted to 
sanity-check my assumptions.

The code (below) creates a number of threads; each thread takes the last (index 
-1) value from a global list of integers, increments it by one and appends the 
new value to the list.

The originator of the code expected that when all the threads completed, the 
list would be an ascending sequence of integers, for example if the original 
list was [0] and two threads mutated it twice each, the final state would be 
[0, 1, 2, 3, 4].

Here is a version of the code (slightly simplified and modified to allow 
changing the number of threads and mutations).


import sys
import threading


class myThread(threading.Thread):

    def __init__(self, nmutations):
        threading.Thread.__init__(self)
        self.nmutations = nmutations

    def run(self):
        mutate(self.nmutations)
        # print (L)
        return


def mutate(nmutations):
    n = nmutations
    while n:
        L.append(L[-1 ]+ 1)
        n -= 1
    return


def main(nthreads=2, nmutations=2):
    global L
    L = [0]
    threads = [myThread(nmutations) for i in range(nthreads)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    print(L)
    assert L == list(range((nthreads * nmutations) + 1))

if __name__ == '__main__':
    nthreads, nmutations = int(sys.argv[1]), int(sys.argv[2])
    main(nthreads, nmutations)

Firstly, is it true that the statement

L.append(L[-1 ]+ 1)

is not atomic, that is the thread might evaluate L[-1] and then yield, allowing 
another thread to mutate L, before incrementing and appending?

Secondly, the original code printed the list at the end of a thread's run 
method to examine the state of the list.  I don't think this would work quite 
as expected, because the thread might yield after mutating the list but before 
printing, so the list could have been mutated before the print was executed.  
Is there a way to display the state of the list before any further mutations 
take place?

(Disclaimer: I understand that sanity, mutable global state and threads are 
unlikely bedfellows and so promise never to try anything like this in 
production code).

Cheers,

Kev




More information about the Python-list mailing list