python threading.Lock() 会锁定所有需要锁定的东西吗?
Does python threading.Lock() lock everything that needs locking?
下面的脚本是抽象的。我的问题是关于 threading.Lock()
的使用
锁定限制了对 "shared resources" 的访问,但我对这能走多远感到紧张。在这个例子中,我有 objects 属性,这些属性是具有数组 属性的对象列表。在某些情况下,依赖关系会更远。
Lock()
"know" 确定需要锁定的所有内容吗?
还显示了以下脚本的输出。该脚本的目的主要是为了讨论——它不会失败,但我不确定它是否锁定了它需要的一切。
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]
您似乎误解了锁的工作原理。
锁不锁定任何对象,它可以只锁定线程执行。
第一个尝试进入 with locker:
块的线程成功。
如果另一个线程试图进入一个 with locker:
块(具有相同的 locker
对象),它会延迟到第一个线程退出该块,因此两个线程都不能更改变量的值同时在块内。
这里你的 "shared resources" 是你在块中更改的变量:如我所见,objects
和 obj.array
。您基本上是在保护它们免受并发访问(即 - 在 python 版本中,初学者没有 GIL)只是因为一次只有一个线程可以更改它们
老朋友称之为临界区,其中一次只能执行 1 个线程。
请注意,对不同的资源使用相同的 locker
对象有点可疑。这有更多的机会死锁/比它需要的更慢。
(如果你嵌套 2 个 with locker
调用,你会遇到死锁 - 如果你想这样做,你需要一个 RLock)
就这么简单。
一把锁不知道您正在尝试做的任何事情。它只是一把锁,它不在乎你把它放在哪里。
例如,您可以声明:
lock = threading.Lock()
然后:
with lock:
# Do stuff.
# In another thread
with lock:
# Do something else
所有其他带有with lock
的块都不能执行,除非当前块完成,它与块中的内容无关。因此对于这个实例,假设第一个 #Do stuff
是 运行ning,当第二个线程命中 with lock: # Do something else
时,它不会 运行 因为有相同的锁。如果您正在进行面向对象的编程,使用 self.lock
是个好主意。
更好的说,multiprocessing Lock不是一把锁,而是一把钥匙。
如果我们有几个代码块的一把钥匙,我们只能用它一次打开其中一个(锁或钥匙获取)。当密钥忙时,其他要求相同密钥的代码块将无法执行并等待。所以它只会阻塞一些代码,阻止解释器执行它。
下面的脚本是抽象的。我的问题是关于 threading.Lock()
锁定限制了对 "shared resources" 的访问,但我对这能走多远感到紧张。在这个例子中,我有 objects 属性,这些属性是具有数组 属性的对象列表。在某些情况下,依赖关系会更远。
Lock()
"know" 确定需要锁定的所有内容吗?
还显示了以下脚本的输出。该脚本的目的主要是为了讨论——它不会失败,但我不确定它是否锁定了它需要的一切。
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]
您似乎误解了锁的工作原理。
锁不锁定任何对象,它可以只锁定线程执行。
第一个尝试进入 with locker:
块的线程成功。
如果另一个线程试图进入一个 with locker:
块(具有相同的 locker
对象),它会延迟到第一个线程退出该块,因此两个线程都不能更改变量的值同时在块内。
这里你的 "shared resources" 是你在块中更改的变量:如我所见,objects
和 obj.array
。您基本上是在保护它们免受并发访问(即 - 在 python 版本中,初学者没有 GIL)只是因为一次只有一个线程可以更改它们
老朋友称之为临界区,其中一次只能执行 1 个线程。
请注意,对不同的资源使用相同的 locker
对象有点可疑。这有更多的机会死锁/比它需要的更慢。
(如果你嵌套 2 个 with locker
调用,你会遇到死锁 - 如果你想这样做,你需要一个 RLock)
就这么简单。
一把锁不知道您正在尝试做的任何事情。它只是一把锁,它不在乎你把它放在哪里。
例如,您可以声明:
lock = threading.Lock()
然后:
with lock:
# Do stuff.
# In another thread
with lock:
# Do something else
所有其他带有with lock
的块都不能执行,除非当前块完成,它与块中的内容无关。因此对于这个实例,假设第一个 #Do stuff
是 运行ning,当第二个线程命中 with lock: # Do something else
时,它不会 运行 因为有相同的锁。如果您正在进行面向对象的编程,使用 self.lock
是个好主意。
更好的说,multiprocessing Lock不是一把锁,而是一把钥匙。 如果我们有几个代码块的一把钥匙,我们只能用它一次打开其中一个(锁或钥匙获取)。当密钥忙时,其他要求相同密钥的代码块将无法执行并等待。所以它只会阻塞一些代码,阻止解释器执行它。