3

I'm trying to understand the nuances of how threads interact the variable space they share when running in the same process. The following code shows two functions, prod and consum, that are spun off in different threads. The two functions are given the same list and lock argument: they use da_list to share data and they use the lockguy to synchronize/thread-safe the sharing.

My main question is: when I run this code, it prints out (1, 0, 0, 0, 0 ...) instead of the (1,2,3,4,5,6 ...) I'm expecting. When I remove the l =[0] line in the consum function, I get the expected behavior. Why does the l = [0] screw it up? When consum finishes, da_list should be [0]. A subsequent call of prod should reset da_list to [da_int]. Thanks for the help.

import threading
import time

    def prod(l,lock):
        da_int = 0
        while True:
            with lock:
                time.sleep(0.1)
                da_int += 1
                l[0] = da_int

    def consum(l,lock):

        data = ''

        while True:
            with lock:
                time.sleep(0.1)
                print(l[0])
                l = [0]

    def main():

        da_list = [0]
        lockguy = threading.Lock()


        thread1 = threading.Thread(target = prod, args=(da_list,lockguy))
        thread2 = threading.Thread(target = consum, args=(da_list,lockguy))
        thread1.daemon = True
        thread2.daemon = True
        thread1.start()
        thread2.start()

        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            pass
        finally:
            print('done')




    if __name__ == '__main__':
        main()
cgiustini
  • 33
  • 1
  • 4
  • Because you don't modify the list that lies under `l` variable in `consum` but you modify the local variable `l` inside it. I.e. `l = [0]` **does not** modify the shared `l` list. It creates a new list inside `consum` that is not shared with `prod`. Note that for example `l[:] = []` **will** modify the underlying list. You could also read this: http://stackoverflow.com/questions/1400608/how-to-empty-a-list-in-python – freakish Jan 28 '15 at 18:12

1 Answers1

5
l = [0]

You seem to be confused about how assignment works in Python. You seem to think that the above line modifies the object to which l was previously bound. It does not.

The above line creates a new list and binds the local name l to it. Whatever object l may have been bound to previously is no longer related to the name l. Any subsequent use of l in this scope will refer to this newly created list.

Consider this single-threaded code:

a = b = [1]  # a and b are both bound to the same list
print a,b    # [1] [1]
b[0] = 2     # modifies the object to which a and b are bound
print a,b    # [2] [2]
b = [0]      # now b is bound to a new list
print a,b    # [2] [0]

Notice how b[0] = 2 and b = [0] differ. In the first one, the object to which b is bound is modified. In the second, b is bound to a whole new object.

Similarly, l = [0] in your code binds l to a new object and you have lost and cannot regain any reference you had to the original object.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Ok yes I see how that would work. In the scope of main(), l gets bound to the value bound to da_list in the thread1 and thread2 start calls. In the scope of function consum, writing l = [0] unbinds l in that scope from the object in memory that is the l in the scope of function prod is bound to. – cgiustini Jan 31 '15 at 00:28