根据cppreference.com,
std::thread
没有参数的构造函数意味着:
创建不代表线程的新线程对象。
我的问题是:
thread
,我们怎样才能“稍后”分配一个线程函数?thread
指定一个“运行”的函数。thread
,但调用“run()”方法以便稍后实际执行该线程。为什么std::thread
没有以这种方式设计?答案 0 :(得分:9)
你的问题表明可能存在一些混淆,明确将执行线程的概念与std::thread
类型分开,并将两者的概念分开是有帮助的。一个“线程函数”。
std::thread
的对象可以与执行线程相关联,或者它可以是“空”而不是引用任何执行线程 std::thread
对象的构造函数。
- 为什么我们需要这个构造函数?
醇>
构造不引用执行线程的空状态。您可能希望拥有一个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
对象管理。
- 为什么我们没有“run(function_address)”方法,以便在没有参数构造时,我们可以为该线程指定一个“运行”的函数。
醇>
因为你不需要它。您可以通过使用参数构造std::thread
对象来启动新的执行线程。如果您希望该执行线程与现有对象相关联,那么您可以通过移动分配或交换来实现。
- 或者,我们可以使用可调用参数(函数,函子等)构造一个线程,但是稍后调用“run()”方法来实际执行该线程。为什么std :: thread不是这样设计的?
醇>
为什么要这样设计?
std::thread
类型用于管理执行线程,而不是持有可调用对象供以后使用。如果要创建一个可以在新的执行线程上运行的可调用对象,有很多方法可以在C ++中使用(使用lambda表达式,或std::bind
,或者std::function
或std::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());
- 为什么我们没有“run(function_address)”方法,以便在没有参数构造时,我们可以为该线程指定一个“运行”的函数。
醇>
我不知道,为什么它是这样设计的,但我没有看到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创建的线程“成为”一个带有移动构造函数的“真实”线程。
如果您需要/喜欢,可以在标准容器中使用线程。