Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/138.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 线程绑定到类成员函数的线程包装器的向量_C++_Multithreading_C++11_Vector - Fatal编程技术网

C++ 线程绑定到类成员函数的线程包装器的向量

C++ 线程绑定到类成员函数的线程包装器的向量,c++,multithreading,c++11,vector,C++,Multithreading,C++11,Vector,今天我遇到了一个有趣的问题,我正在寻找一个“好”的解决方案。我知道最初的问题,但我仍然无法找到一个不糟糕的解决方案 我试图维护包装线程的类的std::vector。到目前为止,这还不错,因为使用C++11的移动语义,我们可以做到这一点。 无论如何,我的问题在于,由线程执行的函数是线程包装器类的成员函数 这产生了一个不可预见的问题,即由于std::vector对象内部可能发生的移动(例如,在内部重新分配存储时),我的包装器类的成员可以完美地移动,但线程确实仍然绑定到它所从的对象 我用一个小例子来说

今天我遇到了一个有趣的问题,我正在寻找一个“好”的解决方案。我知道最初的问题,但我仍然无法找到一个不糟糕的解决方案

我试图维护包装线程的类的std::vector。到目前为止,这还不错,因为使用C++11的移动语义,我们可以做到这一点。 无论如何,我的问题在于,由线程执行的函数是线程包装器类的成员函数

这产生了一个不可预见的问题,即由于std::vector对象内部可能发生的移动(例如,在内部重新分配存储时),我的包装器类的成员可以完美地移动,但线程确实仍然绑定到它所从的对象

我用一个小例子来说明我的问题(见下文)。 然而,我知道有三种可能的解决办法:

  • 预先分配足够大的std::vector,以避免重新分配
    workers.reserve(10)
    例如,在创建线程之前
    con:在“运行时”(生成线程后)期间不可调整

  • 避免移动辅助对象(从而保留线程绑定实例和承载线程的实例的关联),例如,将它们包装到保留其成员的已启用移动的类型中:
    例如,在
    std::unique_ptr
    中包装工人,但任何类似指针的东西都可以完成此任务
    con:由于包装器而浪费资源和可读性,需要为每个工人分配资源

  • 将线程绑定函数放在worker的另一个成员对象中,该对象不嵌入worker类,而是通过指针引用,从而保留线程操作的对象,并将该对象从一个worker移动到另一个worker

    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
      // ...
    };
    
    con:要求每个工人分配

如果我错了,请纠正我,但我几乎可以肯定问题的根源,但要继续努力找到一个不糟糕的解决方案


编辑:我确实希望保留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