C++ 线程绑定到类成员函数的线程包装器的向量
今天我遇到了一个有趣的问题,我正在寻找一个“好”的解决方案。我知道最初的问题,但我仍然无法找到一个不糟糕的解决方案 我试图维护包装线程的类的std::vector。到目前为止,这还不错,因为使用C++11的移动语义,我们可以做到这一点。 无论如何,我的问题在于,由线程执行的函数是线程包装器类的成员函数 这产生了一个不可预见的问题,即由于std::vector对象内部可能发生的移动(例如,在内部重新分配存储时),我的包装器类的成员可以完美地移动,但线程确实仍然绑定到它所从的对象 我用一个小例子来说明我的问题(见下文)。 然而,我知道有三种可能的解决办法:C++ 线程绑定到类成员函数的线程包装器的向量,c++,multithreading,c++11,vector,C++,Multithreading,C++11,Vector,今天我遇到了一个有趣的问题,我正在寻找一个“好”的解决方案。我知道最初的问题,但我仍然无法找到一个不糟糕的解决方案 我试图维护包装线程的类的std::vector。到目前为止,这还不错,因为使用C++11的移动语义,我们可以做到这一点。 无论如何,我的问题在于,由线程执行的函数是线程包装器类的成员函数 这产生了一个不可预见的问题,即由于std::vector对象内部可能发生的移动(例如,在内部重新分配存储时),我的包装器类的成员可以完美地移动,但线程确实仍然绑定到它所从的对象 我用一个小例子来说
- 预先分配足够大的std::vector,以避免重新分配
例如,在创建线程之前workers.reserve(10)
con:在“运行时”(生成线程后)期间不可调整 - 避免移动辅助对象(从而保留线程绑定实例和承载线程的实例的关联),例如,将它们包装到保留其成员的已启用移动的类型中:
例如,在
中包装工人,但任何类似指针的东西都可以完成此任务std::unique_ptr
con:由于包装器而浪费资源和可读性,需要为每个工人分配资源 - 将线程绑定函数放在worker的另一个成员对象中,该对象不嵌入worker类,而是通过指针引用,从而保留线程操作的对象,并将该对象从一个worker移动到另一个worker
con:要求每个工人分配class worker { struct exec_context { // the whole execution context is here... void work() { // this is the thread bound function } }; exec_context *ctx_; // allocate this in default constructor // move the pointer in move constructor // ... };
编辑:我确实希望保留std::vector以进行恒定时间随机访问
一如既往:非常感谢您的帮助!:-) 谢谢,
塞巴斯蒂安
std::list怎么样?或者std::deque如果您总是可以添加新元素,那么它是开始还是结束?我个人会使用std::unique_指针。没错,这会有所帮助。我没有提到我确实希望保持std::vector用于恒定时间(=O(1))访问。(更新)我同意Richard Criten的观点。我没有足够的c++11支持来运行您的代码,但是如果出于某种原因您需要vector,我希望vector能够解决您的问题,因为底层对象不会更改,而这似乎是您的线程所需要的。请回答!:-)您提到的解决方案就是我在方法2中所说的。它绝对可以解决这个问题,但正如在原始帖子中所写的那样,它对代码的混淆有点太多(这里可能不同意,但我仍然认为…@RichardCritten似乎没有更好的解决方案。如果您发布您的答案,我会将其标记为解决方案:-)
#include <thread>
#include <vector>
#include <chrono>
#include <iostream>
using namespace std::literals;
class worker
{
public:
// default constructor, used by emplace
worker()
: thread_{&worker::work, this}
{ }
// resource like handlin, forbid copy semantics
worker(const worker&) = delete;
worker& operator=(const worker&) = delete;
// allow for move construction (requirement for std::vector-container)
// will be invoked, when std::vector reallocates its internal storage
worker(worker &&other)
: thread_(std::move(other.thread_)),
run_(other.run_),
num_(other.num_)
{ }
// we don't want (and need) move assign
worker& operator=(worker&&) = delete;
// indicator: if at any time we get the output 0xdead
// we know that we are operating on an already destructed obejct
virtual ~worker()
{
num_ = 0xdead;
}
// for the sake of completness
void stop()
{
run_ = false;
thread_.join();
}
private:
std::thread thread_;
bool run_ = true;
int num_ = 0;
void work()
{
// output num_ continuesly
while (run_) {
std::printf("%x\n", num_);
std::this_thread::sleep_for(500ms);
}
}
};
int main()
{
std::vector<worker> workers;
// One worker is fine, but more instances will end up in reallocating of the underlying storage and thus in move construction.
workers.emplace_back();
//workers.emplace_back();
std::this_thread::sleep_for(5s);
// again, for the sake of completeness...
for (auto &w : workers)
w.stop();
}
0
0
0
dead
dead
dead
dead
dead
dead
dead
dead
0
dead