我的问题涉及同时使用不同的 vector 。我知道我不能指望同一个 vector 同时在多个线程中工作。我已经分解了该程序,以便更容易理解。我有一个 ThreadClass 类,它有一个构造函数,它只向 vector k 添加一个元素,然后调用一个线程 toCall ,然后输出大小应该是 1 的 vector 。此类的对象是使用 vector 的 push_back
成员在 main()
函数内的不同 vector 内创建的。
输出结果是0。有时我也能得到1。如果我切换到 Debug模式,我可以产生更多的数字 1。我已经在 gnu C++17 编译器 (Ubuntu 16.04) 和 Visual Studio 编译器 (Windows 10) 上测试了这个问题。现在我的问题是,这个示例是否表明我应该完全避免在多线程程序中使用 vector ?
class ThreadClass
{
private:
std::vector<int> k;
std::thread thr;
public:
ThreadClass() {
k.push_back(27);
thr = std::thread(&ThreadClass::toCall, this);
}
void toCall() {
std::cout << k.size() << std::endl;
}
void close() {
if (thr.joinable())thr.join();
}
};
int main(){
std::vector<ThreadClass> lols;
lols.push_back(ThreadClass());
lols[0].close();
return 0;
}
最佳答案
问题是 ThreadClass
类型的值持有对其自身的引用。具体来说,thr
包含 this
的拷贝。
当您复制或移动此类值时,例如当临时ThreadClass()
移入lols
,重复项包含重复项 this
ptr,即它指向旧的临时变量,其生命周期在调用lols.push_back
后结束完成。
我们可以在没有线程的情况下复制这个问题:
class Foo
{
private:
std::vector<int> k;
Foo* possibly_this;
public:
Foo() {
k.push_back(27);
possibly_this = this;
}
void toCall() {
std::cout << possibly_this->k.size() << std::endl;
}
};
int main(){
std::vector<Foo> lols;
lols.push_back(Foo{});
lols[0].toCall();
}
(对我来说,它在 7.3.1 上使用 -O0 打印 0,但同样,它是 UB,因此它可以在您的计算机上执行任何操作。)
lols.emplace()
不会有帮助。如果 std::vector
调整大小,然后指向其中的所有指针/迭代器都将失效。不幸的是,您无法更改存储在 thr
中的指针。 ,所以你只剩下一个解决方案:禁用 ThreadClass
的复制和移动构造函数,如下所示:
//within the definition of ThreadClass
ThreadClass(ThreadClass const&) = delete;
为了放置ThreadClass
在容器中,您将需要额外的间接级别以允许 ThreadClass
类型值的实际对象要有一个稳定的位置。要么 std::list<ThreadClass>
或std::vector<std::unique_ptr<ThreadClass>>
就可以了。
关于c++ - C++ 中不同 vector 之间没有线程安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57014061/