C++ 中的默认构造函数是线程安全的吗?
Is the default constructor thread-safe in C++?
class log_String {
//These are private!
std::vector<std::string> list;
std::mutex m;
log_String& operator=(const log_String &source); //Operatore assegnazione
log_String(const log_String &source);
public:
log_String() {}; // <---- is this thread_safe?
void add(std::string string) {
std::lock_guard<std::mutex> l(m);
list.push_back(string);
}
void printFile(std::string file) {
std::lock_guard<std::mutex> l(m);
std::ofstream myfile;
myfile.open(file);
for (auto iterator = list.begin(); iterator != list.end(); ++iterator) {
myfile << *iterator << "\n";
}
}
};
log_String() {}
是线程安全的吗?
我想即使很多线程同时调用log_String() {}
这应该不是问题吧?我弄错了吗?
如果我错了,可能的解决方案可能是定义它 private
并保护获取新锁的新对象的实例化?
log_String()
基本上是一个函数,但它也是一个构造函数。所以实际上它在对象创建期间的调用意味着递归地调用所有成员变量(具有构造函数)的构造函数,以及所有基 classes 的构造函数,以及它们的成员变量的构造函数。
所以你需要考虑所有被调用的函数。 list
和 m
这两个成员变量应该有线程安全的构造函数,因为它们来自标准库,虽然我没有从标准库中检查(草案应该可以免费下载,如果你想检查你自己),如果他们没有线程安全的构造函数,事情就会变得疯狂。那么就没有基础class,你的构造函数中也没有代码。
结论,它是线程安全的,因为里面没有任何东西,这会导致问题"even if many threads call simultaneously log_String()"。没有可见的共享数据或其他共享资源,如果成员变量中隐藏了任何共享数据,则可以相信它们可以安全地完成。
编写线程不安全的 public 构造函数可能被认为是愚蠢的,甚至是邪恶的。尽管如此,如果您有来自 3rd 方库的成员变量或基 class,或者只是您自己的类型,并且您不能 100% 确定它们的质量,那么值得停下来思考一下这种情况愚蠢已经完成。
一个示例代码,人们可能会合理地编写它,尤其是用于调试目的,并且会使事情变得线程不安全:
private:
static unsigned static_counter;
public:
log_String() {
++static_counter; // not atomic operation! potential data race!
};
为了完整性:对上述代码的修复是简单地使用 std::atomic<unsigned>
作为计数器。更复杂的情况可能需要静态互斥锁(请注意,如果您使用的是旧的蹩脚编译器(至少是 MSVC2010),它可能与静态数据存在不可避免的竞争条件)。
class log_String {
//These are private!
std::vector<std::string> list;
std::mutex m;
log_String& operator=(const log_String &source); //Operatore assegnazione
log_String(const log_String &source);
public:
log_String() {}; // <---- is this thread_safe?
void add(std::string string) {
std::lock_guard<std::mutex> l(m);
list.push_back(string);
}
void printFile(std::string file) {
std::lock_guard<std::mutex> l(m);
std::ofstream myfile;
myfile.open(file);
for (auto iterator = list.begin(); iterator != list.end(); ++iterator) {
myfile << *iterator << "\n";
}
}
};
log_String() {}
是线程安全的吗?
我想即使很多线程同时调用log_String() {}
这应该不是问题吧?我弄错了吗?
如果我错了,可能的解决方案可能是定义它 private
并保护获取新锁的新对象的实例化?
log_String()
基本上是一个函数,但它也是一个构造函数。所以实际上它在对象创建期间的调用意味着递归地调用所有成员变量(具有构造函数)的构造函数,以及所有基 classes 的构造函数,以及它们的成员变量的构造函数。
所以你需要考虑所有被调用的函数。 list
和 m
这两个成员变量应该有线程安全的构造函数,因为它们来自标准库,虽然我没有从标准库中检查(草案应该可以免费下载,如果你想检查你自己),如果他们没有线程安全的构造函数,事情就会变得疯狂。那么就没有基础class,你的构造函数中也没有代码。
结论,它是线程安全的,因为里面没有任何东西,这会导致问题"even if many threads call simultaneously log_String()"。没有可见的共享数据或其他共享资源,如果成员变量中隐藏了任何共享数据,则可以相信它们可以安全地完成。
编写线程不安全的 public 构造函数可能被认为是愚蠢的,甚至是邪恶的。尽管如此,如果您有来自 3rd 方库的成员变量或基 class,或者只是您自己的类型,并且您不能 100% 确定它们的质量,那么值得停下来思考一下这种情况愚蠢已经完成。
一个示例代码,人们可能会合理地编写它,尤其是用于调试目的,并且会使事情变得线程不安全:
private:
static unsigned static_counter;
public:
log_String() {
++static_counter; // not atomic operation! potential data race!
};
为了完整性:对上述代码的修复是简单地使用 std::atomic<unsigned>
作为计数器。更复杂的情况可能需要静态互斥锁(请注意,如果您使用的是旧的蹩脚编译器(至少是 MSVC2010),它可能与静态数据存在不可避免的竞争条件)。