我在Python模块中有类似以下的代码:
_x = None
def set_x():
global _x
_x = # some value (will always be the same)
def worker():
if _x is None:
set_x()
在此模块的客户端中创建的线程是否可以安全地调用worker()?
X在模块的其他地方以只读模式使用;一旦设置它永远不会改变。
是的,我意识到x可以封装在一个类中,并且线程可以在堆栈上创建自己的类实例,从而获得线程安全性,但为了这个问题的目的,假设基于类的实现是不可行或不可取的(例如,模块代表一个单例,其中设置x的成本相对较高,我们希望尽可能不经常设置它。)
答案 0 :(得分:0)
假设您在set_x
中使用全局变量:
x = None
def set_x():
global x
x = "foo"
def worker():
if not x:
set_x()
if not x
与实际设定x之间存在竞争条件。因此,多个线程可以为x
赋值。对于不可变对象来说,这几乎是无害的,但对于像重新分配在现有x
中丢失值的列表这样的可变对象而言是有问题的。通过“大多数”,即使其值比较的对象可能具有不同的ID,因此==
可能有效,但is
可能会失败:
>>> a = "foo"
>>> b = "bar"
>>> c = a + b
>>> d = a + b
>>> c == d
True
>>> c is d
False
当然,不播放set_x
游戏的代码路径可能会发现x
在执行时None
更改为您的预期值。
特别是因为x
的计算成本很高,一个好的方法是将它隐藏在吸气剂后面。现在每个人都必须玩相同的游戏,只有其中一个线程才会真正开展工作。
_x = None
_x_lock = threading.Lock()
def get_x():
global _x
if _x is None:
with _x_lock:
if _x is None:
_x = "foobar"
return _x