修改静态变量是否可以安全线程?

时间:2017-05-07 23:17:38

标签: python c++ c multithreading c++11

从C ++ 11开始,静态变量初始化保证是线程安全的。但是如何在多个线程中修改静态变量呢?如下所示

static int initialized = 0;
Initialize()
{
 if (initialized)
    return;
 initialized = 1; // Is this thread safe? 
}

我问这个问题的原因是我正在阅读源代码 Py_Initialize(),我试图将Python嵌入到多线程C ++应用程序中,我想知道在多个线程中多次调用Py_Initialize()是否安全? Py_Initialize()的实施可归结为 函数_Py_InitializeEx_Private,如下所示

// pylifecycle.c
static int initialized = 0;

_Py_InitializeEx_Private(int install_sigs, int install_importlib)
{
    if (initialized)
        return;
    initialized = 1;
 // a bunch of other stuff
 }

C的结论与C ++相同吗?

修改 所以所有答案都很好,我选择的是最清楚我的头脑。

3 个答案:

答案 0 :(得分:4)

修改静态变量不是线程安全的,但初始化静态变量是线程安全的。所以你可以这样做:

void my_py_init() {
    static bool x = (Py_Initialize(), true);
}

那就是它。您现在可以根据需要从多个线程中调用my_py_initPy_Initialize只会被调用一次。

答案 1 :(得分:2)

Py_Initialize不是线程安全的。只有当你知道Python解释器已经被初始化时,你才可以从多个线程调用它,但如果你能证明调用函数是愚蠢的。

实际上,大多数Python C-API调用都不是线程安全的;您需要获取全局解释器锁(GIL)才能与Python解释器进行交互。 (有关详细信息,请参阅Python C-API docs。请仔细阅读。)

但是,据我所知,在初始化解释器之前,您无法使用标准API来获取GIL。因此,如果您有多个线程,其中任何一个可能初始化相同的Python解释器,您需要使用自己的互斥锁来保护对Py_Initialize的调用。如果可以使用程序逻辑,那么在启动任何线程之前,最好先进行一次初始化。

您引用的代码:

static int initialized = 0;

void Initialize_If_Necessary() 
{
    if (initialized)
        return;
    initialized = 1;
    // Do the initialization only once
}
即使initialized是原子类型,

显然不是任何语言的线程安全。假设两个线程在发生任何初始化之前同时执行此代码:它们都将initialized视为false,因此它们都继续初始化。 (如果您没有两个核心,您可以想象第一个进程是在initialized的测试和分配之间切换的任务。)

答案 2 :(得分:1)

跨多个线程修改静态变量是不安全的,因为如果将变量放入寄存器中,那么其他内核就会被安排。相同寄存器中的信息将不同(修改另一个线程中的变量将与尝试访问该核心的寄存器版本相同,其中包含完全不同的数据)。