线程绑定到类成员函数

时间:2017-02-11 19:50:45

标签: 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 。

    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保持为恒定时间随机访问。

一如既往:非常感谢你的帮助! : - )

谢谢,
塞巴斯蒂安

#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
0
dead
dead
dead
dead
dead
dead
dead
dead
0
dead

0 个答案:

没有答案