以下代码是否在Python中是线程安全的?

时间:2018-05-23 22:07:56

标签: python multithreading thread-safety

我在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的成本相对较高,我们希望尽可能不经常设置它。)

1 个答案:

答案 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