英文:
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保留原始值?
英文:
I’m trying to create a thread (PrinStringManager) which in turn creates several threads (PrintEntry) (depending on the number of elements of the incoming vector of strings).
Each PrintEntry thread created simply prints the string received in the constructor.
It is just a small example representing my problem.
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";
}
What it is happening, is that the object PrinStringManager is created with a vector of 3 strings but when the object function (from PrintStringmanager) is invokek by the thread, the vector is empty:
void operator()() {
for (auto name : vec_) //<-- vec is empty!!
{
vt_.emplace_back(PrintEntry{ name });
}
}
I noticed that when the end of scope for function f() is reached the destructor of PrinStringManager is called and that should be the problem.
Is there a way to o overcome this problem in a simply manner without setting PrinStringManager as static so that it lives forever?
Is there a way to move psm inside the thread object so that when the end of scope of f() is reached, the psm inside the thread hold the original value?
答案1
得分: 4
首先通过添加移动构造函数使您的对象可移动:
```c++
// 由于您有一个析构函数,所以没有隐式声明的移动构造函数,因此将其重新引入
PrinStringManager(PrinStringManager&&) = default;
// 可能还想要移动赋值
PrinStringManager& operator=(PrinStringManager&&) = default;
// (还要修复`PrintEntry`,可能是通过删除析构函数来实现)
然后,您可以使线程持有其自己的PrinStringManager
对象,而不仅仅是堆栈上的引用:
// 线程将持有从psm移动构造的PrinStringManager
std::thread t{ std::move(psm) };
如果由于某种原因无法使PrinStringManager
可移动,您可以使用动态分配:
void f()
{
std::vector<std::string> vec{ "one","two", "three" };
auto psm = std::make_unique<PrinStringManager>(vec);
// 线程持有对`psm`的指针,当线程完成时将其删除
std::thread t{ [psm=std::move(psm)]{ (*psm)(); } };
t.detach();
}
<details>
<summary>英文:</summary>
First make your object movable by adding a move constructor:
```c++
// 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)
Then you can make the thread hold its own PrinStringManager
object, instead of merely a reference to one on the stack:
// The thread will hold a PrinStringManager move-constructed from psm
std::thread t{ std::move(psm) };
If you can't make PrinStringManager
movable for whatever reason, you can use dynamic allocation:
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();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论