如标题中所述,我想知道使用std::async
运行的任务是否可以“重用”空闲线程。
例如,让我们采取下一个代码:
auto task = []() { std::this_thread::sleep_for(std::chrono::seconds(20)); };
int tasksCount = 160;
std::vector<std::future<void>> futures;
for (int i = 0; i < tasksCount; ++i)
{
futures.push_back(std::async(task));
}
所以我们有许多并行运行的任务(160)什么都不做。当此代码在Windows上运行时,它会生成161个等待线程。
无所事事是不是太多线程?为什么等待线程不能“重用”?
答案 0 :(得分:3)
共享确实发生了,但是在核心级别,而不是线程级别。由于你的线程几乎没有计算,所以160个线程可能共享一个CPU核心。
从根本上说,一个线程拥有一个调用堆栈,其中包含每个函数调用的局部变量。这个堆栈实际上无法共享 - 调用堆栈的基本属性是top函数是主动执行的函数。在您的示例中,您在160个堆栈之上有160 sleep_for
。
答案 1 :(得分:2)
粗略地说,一个线程是堆栈的CPU状态和保留存储空间,以及OS调度程序中的一个条目。 C ++语言还包含有关每线程状态(thread_local
)的信息,而辅助库也可能具有某些状态。
这些价格相当昂贵。这些信息不能在线程之间共享;每个线程实际上有一个不同的堆栈,一组不同的thread_local
状态,不同的寄存器值等等。
现在,当一个线程没有执行时,它只是一个表中的一个条目。没有CPU资源(除了由较大的表引起的那些)在线程上花费。所以你有大量的设置成本,一堆线程启动,然后他们去睡觉。调度程序在他们要求睡眠的时间到来之前不会返回这些线程。
因此,在硬件层面,他们正在共享CPU。但是在软件级别,它们的状态不是共享的,而这正是您在调试器中看到的。
答案 2 :(得分:1)
重要的问题是:它会对您的计划产生什么样的可观察差异?该标准不会谈论在低系统级别发生的事情。它只会谈论可观察的行为。那里没有任何收获,唯一可观察到的差异可能是意外的线程局部存储变量混淆。
考虑复杂性:
因此,简而言之,它不会提供任何明显的好处,可能会破坏线程本地存储,具体取决于规范中的说明方式,并且将是一个很大的难点。只是为了减少较低级别的线程数。