我有一个 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;
}
// ...
还节省线程?
我的最后一个问题:我的程序当前仅使用一个互斥体来防止两个线程(相同的函数!)同时运行。我没有对全局变量使用互斥体。这种“情况”是否会导致应用程序崩溃(模块:“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 "";
}
}
正确的解决方案在保罗的回答中。
如果两个线程reading相同,会出现应用程序崩溃吗 多变的?
不,您可以安全地同时读取全局变量(如果您知道没有人同时写入它)。读取操作不会修改全局值,因此它保持不变,并且所有读者“看到”相同的值。
或者仅当一个线程写入到一个线程时才会出现应用程序崩溃 另一个线程当前正在读取的变量?
通常不会,至少不会因为同时进行读取和写入操作。崩溃可能是它的副作用。例如,如果您更新指针值并同时读取它,那么您将尝试访问指针指向的数据。如果读取的值无效,很可能你会崩溃。
此外:是这一部分吗
// ... if (value == "") { m.unlock(); return var; } // ...
还节省线程?
不。您的互斥体
m
仅保护局部变量 value
,由于它是本地变量,因此无需保护。但随后您释放互斥体并复制(read)全局var
变量,而另一个线程可能会写入它。为了使其线程安全,可以使用 std::lock_guard
,然后您就不需要手动锁定/解锁互斥体。或将代码更新为:
m.lock();
if (value == "")
{
string ret_val(var);
m.unlock();
return ret_val;
}
我没有对全局变量使用互斥体。难道是这个 “情况”可能导致应用程序崩溃
正如我之前所写,是的,作为副作用,您的应用程序可能会崩溃。
简单的问题,简单的答案:
如果两个线程读取同一个变量,会出现应用程序崩溃吗?
没有。
仅当一个线程写入另一个线程当前正在读取的变量时,才会出现应用程序崩溃吗?
没有。
但是,您确实应该使用锁和互斥体等来确保获得程序的预期输出。虽然如果一个线程写入另一个线程正在读取的变量,程序本身不会崩溃,但您希望读取线程实际读取什么值?写入/覆盖之前的值还是写入之后的值?
没有互斥锁就不是线程安全的
提出的解决方案仍然不太正确。
var 在互斥体之外读取,并且可以在那时修改。
这看起来像 C++11。如果
std::string
使用共享字符串(C++11 中禁止),则可能会导致读取问题。
在读+写的情况下,如果指针在复制时被修改,则可能会发生 c0000005(访问冲突)。