Python threading.Lock () блокирует все, что требует блокировки? - PullRequest
0 голосов
/ 06 декабря 2018

Сценарий ниже является абстрактным.Мой вопрос касается использования threading.Lock()

Блокировка ограничивает доступ к «общим ресурсам», но я волнуюсь по поводу того, как далеко это заходит.У меня есть атрибуты объектов, которые являются списками объектов, которые имеют атрибуты, которые являются массивами в этом примере.В некоторых случаях зависимость пойдет дальше.

Знает ли Lock() наверняка обо всем, что должно быть заблокировано?

Вывод сценария ниже также показан.Цель сценария в основном для обсуждения - он не ошибается, но я не уверен, что он блокирует все, что ему нужно.

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]

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Замок не знает ничего о том, что вы пытаетесь сделать.Это всего лишь замок, ему все равно, где его надеть.

Например, вы можете объявить:

lock = threading.Lock()

, а затем:

with lock:
    # Do stuff.

# In another thread
with lock:
    # Do something else

Все остальные блоки с with lock не могут быть выполнены, если текущий блок не закончен, он не имеет никакого отношения к тому, что находится в блоке.Так что для этого экземпляра, предполагая, что первый #Do stuff работает, когда второй поток нажмет with lock: # Do something else, он не будет работать, потому что есть такая же блокировка.Использование self.lock - хорошая идея, если вы занимаетесь объектно-ориентированным программированием.

0 голосов
/ 06 декабря 2018

вы, похоже, неправильно понимаете, как работает блокировка.

блокировка не блокирует никакие объекты, она может просто заблокировать поток выполнение .

Первый потоккоторый пытается войти в блок with locker: успешно.

Если другой поток пытается войти в блок with locker: (с тем же объектом locker), он задерживается до тех пор, пока первый поток не выйдет из блока, поэтому обапотоки не могут одновременно изменять значение переменной внутри блока.

Здесь ваши «общие ресурсы» - это переменные, которые вы изменяете в своих блоках: как я вижу, objects и obj.array.Вы в основном защищаете их от одновременного доступа (то есть - в версии Python, где нет GIL для начинающих) только потому, что только один поток может изменить их одновременно

Старожилы называют это критической секцией , где одновременно может выполняться только 1 поток.

Обратите внимание, что несколько сомнительно использовать один и тот же объект locker для разных ресурсов.Это дает больше шансов заблокировать / сделать медленнее, чем нужно.

(а если вы вложите 2 with locker вызовов, вы получите тупик - вам нужен RLock, если вы хотите это сделать)

Это так просто.

...