我正在尝试创建一个线程 (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 保持原始值?
首先通过添加移动构造函数使您的对象可移动:
// 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) };
如果你因为任何原因不能使
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();
}