Why should you lock threads?

后端 未结 2 838
我寻月下人不归
我寻月下人不归 2021-01-02 10:30

I\'ve read a lot of examples on locking threads.. but why should you lock them? From my understanding, when you initiate threads without joining them, they will compete with

2条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2021-01-02 10:48

    First, locks are designed to protect resources; threads aren't 'locked' or 'unlocked' they /acquire/ a lock (on a resource) and /release/ a lock (on a resource).

    You are correct that you want threads to run concurrently as much as possible, but let's take a look at this:

    y=10
    
    def doStuff( x ):
        global y
        a = 2 * y
        b = y / 5
        y = a + b + x
        print y
    
    t1 = threading.Thread( target=doStuff, args=(8,) )
    t2 = threading.Thread( target=doStuff, args=(8,) )
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    

    Now, you might know that either one of these threads could complete and print first. You would expect to see both output 30.

    But they might not.

    y is a shared resource, and in this case, the bits that read and write to y are part of what is called a "critical section" and should should be protected by a lock. The reason is you don't get units of work: either thread can gain the CPU at any time.

    Think about it like this:

    t1 is happily executing code and it hits

    a = 2 * y
    

    Now t1 has a = 20 and stops executing for a while. t2 becomes active while t1 waits for more CPU time. t2 executes:

    a = 2 * y
    b = y / 5
    y = a + b + x
    

    at this point the global variable y = 30

    t2 stops stops for a bit and t1 picks up again. it executes:

    b = y / 5
    y = a + b + x
    

    Since y was 30 when b was set, b = 6 and y is now set to 34.

    the order of the prints is non-deterministic as well and you might get the 30 first or the 34 first.

    using a lock we would have:

    global l
    l = threading.Lock()
    def doStuff( x ):
        global y
        global l
        l.acquire()
        a = 2 * y
        b = y / 5
        y = a + b + x
        print y
        l.release()
    

    This necessarily makes this section of code linear -- only one thread at a time. But if your entire program is sequential you shouldn't be using threads anyway. The idea is that you gain speed up based on the percentage of code you have that can execute outside locks and run in parallel. This is (one reason) why using threads on a 2 core system doesn't double performance for everything.

    the lock itself is also a shared resource, but it needs to be: once one thread acquires the lock, all other threads trying to acquire the /same/ lock will block until it is released. Once it is released, the first thread to move forward and acquire the lock will block all other waiting threads.

    Hopefully that is enough to go on!

提交回复
热议问题