Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does python threading.Lock() lock everything that needs locking?

Script below is abstracted. My question is about the use of threading.Lock()

Locking limits access to "shared resources" but I am nervous about how far that goes. I have objects attributes that are lists of objects which have attributes that are arrays in this example. In some cases the dependency will go farther.

Does Lock() "know" for sure about everything that needs to be locked?

The output of the script below is also shown. The purpose of the script is mostly for discussion - It doesn't fail, but I am not confident that it is Locking everything it needs to.

start:   [array([0, 1]), array([0, 1, 2]), array([0, 1, 2, 3])]
append an object
done!
finish:  [array([505, 605]), array([10, 11, 12]), array([10, 11, 12, 13]), array([5])]


import time
from threading import Thread, Lock
import numpy as np

class Bucket(object):
    def __init__(self, objects):
        self.objects = objects

class Object(object):
    def __init__(self, array):
        self.array = array

class A(Thread):
    def __init__(self, bucket):
        Thread.__init__(self)
        self.bucket      = bucket
    def run(self):
        nloop            = 0
        locker           = Lock()
        n = 0
        while n < 10:
            with locker:  
                objects = self.bucket.objects[:]  # makes a local copy of list each time
            for i, obj in enumerate(objects):
                with locker:
                    obj.array += 1
                time.sleep(0.2)
            n += 1
            print 'n: ', n
        print "done!"
        return

objects = []
for i in range(3):
    ob = Object(np.arange(i+2))
    objects.append(ob)
bucket = Bucket(objects)

locker           = Lock()

a = A(bucket)
print [o.array for o in bucket.objects]

a.start()

time.sleep(3)

with locker:
    bucket.objects.append(Object(np.arange(1)))  # abuse the bucket!
    print 'append an object'
time.sleep(5)

print [o.array for o in bucket.objects]
like image 990
uhoh Avatar asked Dec 06 '18 17:12

uhoh


People also ask

What does lock do in threading Python?

A lock can be locked using the acquire() method. Once a thread has acquired the lock, all subsequent attempts to acquire the lock are blocked until it is released. The lock can be released using the release() method.

What are the limitations of threading in Python?

In fact, a Python process cannot run threads in parallel but it can run them concurrently through context switching during I/O bound operations. This limitation is actually enforced by GIL. The Python Global Interpreter Lock (GIL) prevents threads within the same process to be executed at the same time.

What is the difference between threading lock and threading are lock?

The main difference is that a Lock can only be acquired once. It cannot be acquired again, until it is released. (After it's been released, it can be re-acaquired by any thread). An RLock on the other hand, can be acquired multiple times, by the same thread.

Are locks needed in Python?

At any moment, yes, only one thread is executing Python code (other threads may be executing some IO, NumPy, whatever). That is mostly true. However, this is trivially true on any single-processor system, and yet people still need locks on single-processor systems.

What is a thread lock in Python?

Locking is a synchronization mechanism for threads. Once a thread acquires a lock, no other thread can access the shared resource until and unless it releases it. It helps to avoid the race condition. What is a lock object in Python?

Why do we use locks in threading?

Once thread switching is ordered, access and modification of data between threads becomes controlled, so to ensure thread safety, locks must be used. The threading module provides the five most common types of locks, which are divided by function as follows.

How to synchronize two or more threads in Python?

This lock helps us in the synchronization of two or more threads. Lock class perhaps provides the simplest synchronization primitive in Python. Primitive lock can have two States: locked or unlocked and is initially created in unlocked state when we initialize the Lock object.

What is the use of Lock class in Python?

Lock class perhaps provides the simplest synchronization primitive in Python. Primitive lock can have two States: locked or unlocked and is initially created in unlocked state when we initialize the Lock object. It has two basic methods, acquire () and release ().


1 Answers

you seem to misunderstand how a lock works.

a lock doesn't lock any objects, it can just lock the thread execution.

The first thread which tries to enter a with locker: block succeeds.

If another thread tries to enter a with locker: block (with the same locker object), it's delayed until the first thread exits the block, so both threads cannot change the value of the variable inside the block at the same time.

Here your "shared resources" are the variables you're changing in your blocks: as I can see, objects and obj.array. You're basically protecting them from concurrent access (that is - in a python version where there isn't a GIL for starters) just because only one thread can change them at a time

Old-timers call that a critical section, where only 1 thread can execute at a time.

Note that it's slightly dubious to use the same locker object for different resources. This has more chance to deadlock / be slower that it needs to be.

(and if you nest 2 with locker calls you get a deadlock - you need an RLock if you want to do that)

That's as simple as that.

like image 88
Jean-François Fabre Avatar answered Oct 07 '22 08:10

Jean-François Fabre