提问人:eniac 提问时间:2/23/2023 最后编辑:eniac 更新时间:2/23/2023 访问量:72
当 std::thread 执行 operator() 时,vector 变为空
vector becomes empty when the operator() is executed by std::thread
问:
我正在尝试创建一个线程(PrinStringManager),它又创建了几个线程(PrintEntry)(取决于传入字符串向量的元素数量)。 创建的每个 PrintEntry 线程都只打印构造函数中收到的字符串。
这只是代表我问题的一个小例子。
class PrintEntry
{
public:
PrintEntry(std::string& name) :name_(name) {}
~PrintEntry() {}
void operator()() {
std::cout << "name: " << name_;
}
private:
std::string name_;
};
class PrinStringManager
{
public:
PrinStringManager(std::vector<std::string>& vec) :vec_(vec){}
~PrinStringManager() {
for (auto& t : vt_) t.join();
}
void operator()() {
for (auto name : vec_)
{
vt_.emplace_back(PrintEntry{ name });
}
}
private:
std::vector<std::string> vec_;
std::vector<std::thread> vt_;
};
void f()
{
std::vector<std::string> vec{ "one","two", "three" };
PrinStringManager psm{ vec };
std::thread t{ std::ref(psm) };
t.detach();
}
int main()
{
f();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::cout << "Hello World!\n";
}
它正在发生的事情是,对象 PrinStringManager 是使用 3 个字符串的向量创建的,但是当线程调用对象函数(来自 PrintStringmanager)时,向量是空的:
void operator()() {
for (auto name : vec_) //<-- vec is empty!!
{
vt_.emplace_back(PrintEntry{ name });
}
}
我注意到,当到达函数f()的作用域结束时,会调用PrinStringManager的析构函数,这应该是问题所在。
有没有办法以简单的方式克服这个问题,而无需将 PrinStringManager 设置为静态,以便它永远存在?
有没有办法在线程对象内移动 psm,以便在到达 f() 作用域的末尾时,线程内的 psm 保持原始值?
答:
4赞
Artyer
2/23/2023
#1
首先,通过添加一个移动构造函数,使你的对象可移动:
// No implicitly declared one since you have a destructor, so bring it back
PrinStringManager(PrinStringManager&&) = default;
// Also the move assign is probably wanted
PrinStringManager& operator=(PrinStringManager&&) = default;
// (And also fix `PrintEntry`, probably by removing the destructor)
然后,您可以让线程包含自己的对象,而不仅仅是对堆栈上的一个对象的引用:PrinStringManager
// The thread will hold a PrinStringManager move-constructed from psm
std::thread t{ std::move(psm) };
如果由于某种原因无法使 Movable 可用,则可以使用动态分配:PrinStringManager
void f()
{
std::vector<std::string> vec{ "one","two", "three" };
auto psm = std::make_unique<PrinStringManager>(vec);
// The thread holds a pointer to `psm` which is deleted
// when the thread finishes
std::thread t{ [psm=std::move(psm)]{ (*psm)(); } };
t.detach();
}
评论
0赞
eniac
2/23/2023
已经尝试过了,但编译器抱怨:error C2280: 'std::thread::thread(const std::thread &)': attempting to reference a deleted function
0赞
Artyer
2/23/2023
@eniac 事实证明,您的类是不可移动的(有关详细信息,请参阅 stackoverflow.com/q/33932824,并遵循 0/3/5 规则)
0赞
eniac
2/23/2023
是的,我可以让它移动。已经修复。谢谢。
评论
PrintStringManager
main
f
PrinStringManager
PrinStringManager
psm
psm
t
pfm
t
thread.detach()
vec
f()
psm