Mutable global state and threads

Kev Dwyer kevin.p.dwyer at gmail.com
Wed Jan 4 01: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