全局变量的 RAM 共享行为实际上是全局的吗? [已关闭]

问题描述 投票:0回答:3

当多线程应用程序中有全局变量时,出于性能原因,每个线程是否都有自己的全局变量副本,或者每个线程只能读/写同一版本?

如果答案是前者,互斥锁 Lock() 后跟 Unlock() 能否保证将数据复制到全局变量中?

语言是Cpp。编译器是VS默认的编译器。操作系统是Windows。目标 CPU 是现代 5+ 核心。

许多评论表明,除非另有说明,否则全局变量在整个应用程序运行实例中只有一个副本。该代码是否完全安全,不需要任何同步或任何其他数据访问保护机制? (C++ 伪代码。)(这不是我实际的最佳实践代码,我只是想确保某些概念作为查询的一部分。)

//In MyApplication.cpp
    int Variable1 = 0;
    bool ThreadAFinished = false;

    void ThreadAFunction()
    {
        while (true)
        {
            Variable1++;

            if (Variable1 == 1000)
            {
                break;
            }
        }
        ThreadAFinished = true;
    }
    void ThreadBFunction()
    {
        while (true)
        {
            if (ThreadAFinished)
            {
                if (Variable1 < 1000)
                {
                    printf("ERROR");
                }
                else
                {
                    printf("TEST PASSED");
                }
                break;
            }
        }
    }

我们能保证永远不会出错吗?

c++ multithreading synchronization global
3个回答
2
投票

其他人已经评论过:只要您不明确将其设置为线程本地的,您就可以依赖于让所有线程都访问一份副本。

至于线程竞争问题,请注意,不仅是编译器针对不同的指令顺序进行了优化,而且内存和寄存器缓存也会欺骗您。 不要指望 volatile 修饰符能够解决这些问题 ...它也不能保证在所有情况下都能正确运行。

维基百科中有关于 内存防护 / 内存屏障的好(简短且易于理解)文章,标准库也有一些可供您使用的东西:搜索“原子操作库


1
投票

...每个线程都有自己的全局变量的“副本”吗...

是的,但是不是。

“是”,因为,在低级别,在与 C++ 语言相关的任何内容之下的级别,不同线程对变量的更新可以缓存在不同的内存位置。

“否”,因为“缓存”不是解释 C++ 语言如何工作的一部分。它不是语言内存模型的一部分。

按照内存模型的解释,任何给定的全局变量都有且只有一份副本。但是,当不同的线程在没有显式同步的情况下更新全局变量时,则允许线程对不同变量的更新发生的顺序存在分歧。这种不一致可能会导致线程看到不一致的、有时是损坏的“共享”数据视图,但它也允许系统最有效地利用硬件缓存,并在线程访问其“私有”数据时获得最佳性能. 互斥锁 Lock() 和 Unlock() 能否保证将数据正确复制到全局变量中?

锁定和解锁互斥体是一种显式同步,程序可以使用它来确保不同线程看到同一共享数据结构的一致视图。

如果您有一些共享的全局结构,并且每个线程在访问该结构时都锁定
相同的

共享的全局互斥锁,那么该结构就是安全的。 “安全”的意思是,每次线程 A 锁定互斥体时,它都会看到数据结构处于与其他线程释放互斥体之前其他线程留下的完全相同的状态。

如果任何线程在没有锁定互斥锁的情况下访问该结构,即使它是只读访问,另一个线程可能会看到处于不一致状态的结构,因为它看到的内容的不同部分可能来自不同级别的“缓存” .”

同一个全局变量可以有多个副本。每个处理器都有自己的数据缓存,并且缓存通常会保存处理器正在使用的任何数据的单独副本。


1
投票

问题中的代码存在数据竞争:一个线程更新全局变量,另一个线程读取全局变量的值。程序的行为是未定义的。最简单的修复方法是更改两个全局变量的类型,使它们成为原子变量:

std::atomic<int> Variable1 = 0; std::atomic<bool> ThreaedAFinished = false;

现在编译器(好吧,运行时库)将提供适当的代码来确保缓存得到适当的刷新和重新加载,以便两个全局变量的行为就像每个变量只有一个副本一样。

© www.soinside.com 2019 - 2024. All rights reserved.