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
并保护获取新锁的新对象的实例化?
答案 0 :(得分:2)
log_String()
基本上是一个函数,但它也是一个构造函数。因此实际上它在对象创建期间的调用意味着还会递归地调用所有成员变量(具有构造函数)的构造函数,以及所有基类的构造函数和它们的成员变量的构造函数。
所以你需要考虑被调用的所有函数。两个成员变量list
和m
应该有线程安全构造函数,因为它们来自标准库,而我没有从标准中检查(草稿应该可以免费下载,如果你想要检查自己),如果他们没有线程安全的构造函数,事情就会疯狂。然后没有基类,构造函数中没有代码。
结论,它是线程安全的,因为那里没有任何东西会导致问题“即使很多线程同时调用log_String()”。没有共享数据或其他共享资源可见,如果成员变量中隐藏了任何共享数据,则可以信任它们安全地完成。
编写线程不安全的公共构造函数可能被认为是愚蠢的,甚至是邪恶的。尽管如此,如果您有来自第三方库的成员变量或基类,或者只是您自己的类型,并且您对它们的质量不是100%肯定,那么停止并思考是否已经完成这种愚蠢是值得的
一个可能合理编写的示例代码,特别是出于调试目的,以及哪些会使线程不安全:
private:
static unsigned static_counter;
public:
log_String() {
++static_counter; // not atomic operation! potential data race!
};
为了完整性:修复上述代码只需使用std::atomic<unsigned>
作为计数器。更复杂的情况可能需要静态互斥量(请注意,如果您使用旧的糟糕编译器(至少MSVC2010),这可能与静态数据有不可避免的竞争条件)。