vector becomes empty when the operator() is executed by std::thread

huangapple go评论64阅读模式
英文:

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&amp; name) :name_(name) {}
	~PrintEntry() {}
	void operator()() {
		std::cout &lt;&lt; &quot;name: &quot; &lt;&lt; name_;
	}
private:

	std::string name_;
};

class PrinStringManager
{
public:
	PrinStringManager(std::vector&lt;std::string&gt;&amp; vec) :vec_(vec){}
	~PrinStringManager() {
		for (auto&amp; t : vt_) t.join();
	}

	void operator()() {
		for (auto name : vec_)
		{
			vt_.emplace_back(PrintEntry{ name });
		}
	}

private:

	std::vector&lt;std::string&gt; vec_;
	std::vector&lt;std::thread&gt; vt_;
};


void f()
{
	std::vector&lt;std::string&gt; vec{ &quot;one&quot;,&quot;two&quot;, &quot;three&quot; };
	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 &lt;&lt; &quot;Hello World!\n&quot;;
}

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_) //&lt;-- 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&amp;&amp;) = default;
    // Also the move assign is probably wanted
    PrinStringManager&amp; operator=(PrinStringManager&amp;&amp;) = 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&lt;std::string&gt; vec{ &quot;one&quot;,&quot;two&quot;, &quot;three&quot; };
    auto psm = std::make_unique&lt;PrinStringManager&gt;(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();
}

huangapple
  • 本文由 发表于 2023年2月23日 19:24:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75544159.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定