c++理解多线程与全局变量

C++ understanding multithreading with global variables

本文关键字:全局变量 多线程 c++      更新时间:2023-10-16

我有一个c++程序声明了一些全局变量。之后,它分成几个线程来执行几个任务。这些线程读取和写入这些全局变量中的一些。

如果两个线程读取相同的变量,会有应用程序崩溃吗?或者只有当一个线程写入另一个线程当前正在读取的变量时,才会出现应用程序崩溃?

所以,如果我的第二个问题的答案是肯定的,下面的代码示例能解决这个问题吗?

#include <string>
#include <thread>
#include <mutex>
using namespace std;
mutex m;
string var = "foo";
// function to provide read and write access
// "protected" with mutex
string test(string value = "")
{
    m.lock();
    if (value == "")
    {
        m.unlock();
        return var;
    }
    else
    {
        var = value;
        m.unlock();
        return "";
    }
}
void thread1()
{
    // use global variable local
    string localVar = test();
}
void thread2()
{
    // overwrite global variable
    test("bar");
}
void thread3()
{
    // use global variable local
    string localVar = test();
}
int main()
{    
    thread t1(thread1);
    thread t2(thread2);
    thread t3(thread3);
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

进一步:这部分是

// ...
if (value == "")
{
    m.unlock();
    return var;
}
// ...

也thread-save吗?

我的最后一个问题:我的程序目前只使用一个互斥锁来防止两个线程(同一个函数!)同时运行。我不使用互斥锁为我的全局变量。可能是这种"情况"会导致应用程序崩溃(模块:"ntdll.dll")异常代码0xc0000005 ?

提前感谢!

多次读取总是线程安全的。一旦一个线程正在写一个非原子变量var,而其他线程正在从var读取,你就处于竞争条件的危险之中。因此,您几乎可以做到这一点,但请使用互斥锁保护(它们是RAII,因此异常安全且更简洁的c++),例如:

#include <mutex>
#include <string>
// ...
std::mutex m;
std::string var = "foo";
// ...
std::string test(const std::string& value = "")
{
    std::lock_guard<std::mutex> lock(m);
    if (value == "")
    {
        return var;
    }
    else
    {
        var = value;
        return "";
    }
}
如果两个线程读取相同的变量,会有应用程序崩溃吗?

。从来没有。如果你只从多个线程读取,你总是安全的。

只有当一个线程写入另一个线程当前正在读取的变量时,才会有应用程序崩溃吗?

不完全是这样,但是可以导致崩溃,这就是代码中发生的事情。从使应用程序从多个线程同时读/写的角度来看,这并不"危险"。最坏的情况是在某些地方得到了垃圾值。这本身不会让你的应用崩溃,但它最终肯定会导致崩溃。问题是当您读取的数据具有原始值以外的含义时(例如整数)。例如,如果您正在读取内存地址(指针),然后尝试访问该地址的内存,但是内存已经被释放,那么您就有麻烦了-这就是代码中发生的情况。字符串的字符已经移动到一个新地址,但是你试图读取旧地址。

<罢工>为了解决这个问题,您应该将整个操作包装在锁中,并且可以为此使用一个临时变量:

string test(string value = "")
{
    m.lock();
    if (value == "")
    {
        string temp = var;
        m.unlock();
        return temp;
    }
    else
    {
        var = value;
        m.unlock();
        return "";
    }
}

正确的答案在Paul的答案中。

如果两个线程读取相同,会有应用程序崩溃吗变量?

不,你可以安全地同时读取全局变量(如果你知道没有人同时写它)。读操作不修改全局值,所以它保持不变,所有的读取器"看到"的是相同的值。

或者只有当一个线程写入a时才会有应用程序崩溃另一个线程当前正在读取的变量?

通常不会,至少不会因为同时的读和写操作。坠机可能是它的副作用。例如,如果您更新指针值并同时读取它,那么您将尝试访问指针所指向的数据。如果读值无效,很可能导致系统崩溃。

进一步:这部分是

// ...
if (value == "")
{
    m.unlock();
    return var;
}
// ...

也thread-save吗?

。互斥锁m只保护局部变量value,因为它是局部的,所以不需要保护。但是随后你释放互斥锁并拷贝()全局var变量,而另一个线程可以写它。为了使它线程安全,要么使用std::lock_guard,然后你就不需要手动锁定/解锁互斥锁。或者将代码更新为:

m.lock();
if (value == "")
{
    string ret_val(var);
    m.unlock();
    return ret_val;
}

我没有为我的全局变量使用互斥锁。会是这个吗"situation"可能导致应用程序崩溃

正如我之前所写的,是的,作为一个副作用,你的应用程序可能会崩溃。

简单问题,简单答案:

会有一个应用程序崩溃,如果两个线程正在读取相同的变量?

只有当一个线程写入另一个线程正在读取的变量时,才会有应用程序崩溃吗?号

然而,你真的应该使用锁和互斥锁等来确保你得到预期的程序输出。虽然程序本身不会崩溃,如果一个线程写入一个变量正在被另一个线程读取,您希望读取线程实际读取什么值?之前的值被写入/重写或之后的值?

没有互斥锁就不是线程安全的

建议的解决方案仍然不太正确。

var在互斥锁外被读取,并且可以在那个时候被修改。

这看起来像c++ 11。如果std::string使用共享字符串(c++ 11禁止),可能会导致读取问题。

在读+写的情况下,如果指针在复制时被修改,则可能发生c0000005(访问冲突)。