【问题标题】:Vector of elements containing std::threads包含 std::threads 的元素向量
【发布时间】:2018-07-27 08:28:28
【问题描述】:

我有一个类 Tester 包含一个 std:thread 对象和一个 std::vectorTester。我知道我不能复制线程,所以push_back 是不可能的,但为什么emplace_back 不起作用?我的代码中的副本在哪里?

#include <iostream>
#include <thread>
#include <vector>
#include <functional>
#include <unistd.h>

class Tester
{
public:
    Tester(std::function<void(void)> func) : 
        th(func)
    {
    }

    ~Tester()
    {
        th.join()
    }

private:
    std::thread th;
};

std::vector<Tester> testers;

void InnerHelloWorld()
{
    std::cout << "Hello from the inner word!\n";
}

int main() {
    std::cout << "Hello World!\n";

    for(size_t i = 0 ; i < 4 ; i++)
    {
        testers.emplace_back(InnerHelloWorld);
    }

    sleep(1);

    return 0;
}

【问题讨论】:

  • 另外,你可以在这里测试一下:repl.it/repls/QuietTidyScientificcomputing
  • 你必须写一个移动构造函数,这里没有隐式声明。
  • 顺便说一句,push_back 可以很好地与右值配合使用(即,如果您有移动 c'tor),但出于风格原因,我更喜欢 emplace_back

标签: c++ multithreading c++11


【解决方案1】:

您的代码中有几个小问题

您错过了以下分号:

th.join()

但重要的是,你需要给你的类一个移动构造函数——默认的就可以了:

Tester(Tester&&) = default;

这是需要的,因为当向量调整自身大小时,它们需要移动或复制其元素。 通常会为您创建一个移动构造函数,但在您的情况下,使用自定义析构函数会抑制它。见here

这将使您的代码编译,但随后会在运行时引发异常。这是因为您有时会从 move-from Testers 破坏,这将在已移动的线程上调用 join。幸运的是,这是一个简单的解决方法:

~Tester()
 {
   if(th.joinable())
       th.join();
 }

完整的工作代码:

#include <iostream>
#include <thread>
#include <vector>
#include <functional>

#include <unistd.h>

class Tester
{
  public:
  Tester(std::function<void(void)> func) : 
    th(func)
  {
  }

  ~Tester()
  {
    if(th.joinable())
        th.join();
  }

  Tester(Tester&&) = default;

  private:
  std::thread th;
};

std::vector<Tester> testers;

void InnerHelloWorld()
{
  std::cout << "Hello from the inner word!\n";
}

int main() {
  std::cout << "Hello World!\n";

  for(size_t i = 0 ; i < 4 ; i++)
  {
    testers.emplace_back(InnerHelloWorld);
  }

  sleep(1);

  return 0;
}

【讨论】:

  • 这几乎解决了我的问题......在我的真实代码中,我有一个std::thread 和一个std::atomic_bool,没有定义复制构造函数,也没有定义右值
  • @MikeVine FWIW,用户声明的析构函数防止隐式声明移动 c'tor。
  • @ErniBrown 将atomic 放入向量中几乎肯定是个坏主意(除非向量容量不变)。原子用于线程同步,这意味着它们的地址必须是稳定的。原子不可复制构造的原因是复制变量不能在任何可以安全使用锁的通用硬件上以原子方式实现,顺便说一句。虽然通过为 move c'tor 提供自定义定义很容易解决这个问题,但您确实应该重新考虑您的设计。
  • @ArneVogel 谢谢,我使用指向对象的 unique_ptr 向量解决了。
【解决方案2】:

您需要为您的类定义移动构造函数,使其变为 MoveInsertable 并满足emplace 方法的要求:

Tester(Tester && other) : 
    th(::std::move(other.th))
{
}

修复缺少移动构造函数后将出现的另一个问题是尝试加入不可加入的线程,因为实际线程可能已被移动到另一个对象中。所以需要添加相应的检查:

~Tester()
{
   if(th.joinable())
   {
       th.join();
   }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-13
    相关资源
    最近更新 更多