没有参数的std :: thread构造函数

时间:2016-01-27 12:42:49

标签: c++ multithreading c++11 stdthread

根据cppreference.comstd::thread没有参数的构造函数意味着:

  

创建不代表线程的新线程对象。

我的问题是:

  1. 为什么我们需要这个构造函数?如果我们使用这个构造函数创建thread,我们怎样才能“稍后”分配一个线程函数?
  2. 为什么我们没有“run(function_address)”方法,以便在没有参数构造时,我们可以为thread指定一个“运行”的函数。
  3. 或者,我们可以使用可调用参数(函数,函子等)构造thread,但调用“run()”方法以便稍后实际执行该线程。为什么std::thread没有以这种方式设计?

4 个答案:

答案 0 :(得分:9)

你的问题表明可能存在一些混淆,明确将执行线程的概念与std::thread类型分开,并将两者的概念分开是有帮助的。一个“线程函数”。

  • 执行线程表示通过程序的控制流,可能对应于内核管理的操作系统线程。
  • 类型std::thread的对象可以与执行线程相关联,或者它可以是“空”而不是引用任何执行线程
  • 标准C ++中没有“线程函数”这样的概念。任何函数都可以在新的执行线程中运行,方法是将它传递给std::thread对象的构造函数。
  
      
  1. 为什么我们需要这个构造函数?
  2.   

构造不引用执行线程的空状态。您可能希望拥有一个std::thread类的成员变量,但不想立即将其与执行线程关联。因此,您默认构造它,然后稍后启动一个新的执行线程并将其与std::thread成员变量相关联。或者您可能想要这样做:

std::thread t;
if (some_condition) {
  t = std::thread{ func1, arg1 };
}
else {
  auto result = some_calculation();
  t = std::thread{ func2, arg2, result };
}

默认构造函数允许创建对象t,而无需启动新的执行线程,直到需要为止。

  

如果我们使用这个构造函数创建一个线程,我们怎么能在以后“分配”一个线程函数?

使用“作业”“分配”: - )

但是你没有给它分配一个“线程函数”,这不是std::thread的用途。您为其指定了另一个std::thread

std::thread t;
std::thread t2{ func, args };
t = std::move(t2);

考虑创建新的执行线程而不是“为线程函数分配”。你不只是分配一个函数,那就是std::function将用于什么。您正在请求运行时创建一个新的执行线程,该线程将由std::thread对象管理。

  
      
  1. 为什么我们没有“run(function_address)”方法,以便在没有参数构造时,我们可以为该线程指定一个“运行”的函数。
  2.   

因为你不需要它。您可以通过使用参数构造std::thread对象来启动新的执行线程。如果您希望该执行线程与现有对象相关联,那么您可以通过移动分配或交换来实现。

  
      
  1. 或者,我们可以使用可调用参数(函数,函子等)构造一个线程,但是稍后调用“run()”方法来实际执行该线程。为什么std :: thread不是这样设计的?
  2.   

为什么要这样设计?

std::thread类型用于管理执行线程,而不是持有可调用对象供以后使用。如果要创建一个可以在新的执行线程上运行的可调用对象,有很多方法可以在C ++中使用(使用lambda表达式,或std::bind,或者std::functionstd::packaged_task或自定义仿函数类型)。 std::thread的工作是管理执行线程,在你想要调用它之前不要保留可调用对象。

答案 1 :(得分:4)

提供默认构造函数,以便可以创建“空”thread对象。并非所有thread个对象都与构造所述对象时的执行线程相关联。考虑何时thread是某种类型的成员,并且该类型具有默认构造函数。考虑另一种情况,thread类型没有“挂起”线程的概念,即它不能在挂起状态下创建。

thread类型没有某种“运行”方法,因为原始设计决策(IIRC)之一是在thread对象和thread对象之间建立“强”关联。执行的线程。允许thread被“移动”使得该意图更清晰(在我看来)。因此,将thread对象的实例移动到“空”对象比尝试“运行”std::thread更清晰。

可以想象你可以创建一个提供“run”方法的某种类型的包装类,但我认为这可能是一个更窄的用例,并且可以在给定{{}的API的情况下解决它。 1}}类。

答案 2 :(得分:2)

编辑:也许让我们在最后一部分发表评论:

  

3.2为什么std :: thread不是这样设计的?

我不知道究竟为什么(确实存在优势和不利因素 - 请参阅Jonathan Wakely关于其背后理性的更多详细信息的答案),但似乎c ++ 11 std::thread更接近模型pthreads比例如QT或Java的QThread/Thread类,可能是您混淆的根源。

关于你的其他问题:

  

1.1为什么我们需要这个构造函数?

您可能想要创建一个std::thread变量,但不要直接启动一个线程(例如一个类成员变量或一个静态数组的元素,由alangab显示)。与没有文件名可以创建的std::fstream没什么不同。

  

1.2如果我们使用这个构造函数创建一个线程,我们怎么能在以后“分配”一个线程函数?

例如:

std::thread myThread;

// some other code

myThread = std::thread(foo()); 
  
      
  1. 为什么我们没有“run(function_address)”方法,以便在没有参数构造时,我们可以为该线程指定一个“运行”的函数。
  2.   

我不知道,为什么它是这样设计的,但我没有看到run方法与上述语法相比的好处。

  

3.1或者,我们可以使用可调参数(函数,函子等)构造一个线程,但是稍后调用“run()”方法来实际执行该线程。

您可以通过创建lambda或std::function对象来模拟这个,并在您想要运行该函数时创建该线程。

auto myLambda = [=]{foo(param1, param2);};
// some other code
std::thread myThread(myLambda);

如果你想使用你描述的语法,我建议你编写自己的Thread包装类(应该只需几行代码)(可选)也确保线程是在包装破坏时分离或加入,这在我看来是std::thread的主要问题。

答案 3 :(得分:2)

默认构造函数为您提供了创建线程数组的可能性:

thread my_threads[4];

for (int i=0; i<4; i++)
{
   thread temp(func,...);
   my_threads[i]=move(temp);
}

使用默认的costructor创建的线程“成为”一个带有移动构造函数的“真实”线程。

如果您需要/喜欢,可以在标准容器中使用线程。