在一次代码评审中,我遇到了一段代码,基本上可以归结为:
#include <iostream>
#include <future>
#include <thread>
int main( int, char ** )
{
std::atomic<int> x( 0 );
std::future<void> task;
for( std::size_t i = 0u; i < 5u; ++i )
{
task = std::async( std::launch::async, [&x, i](){
std::this_thread::sleep_for( std::chrono::seconds( 2u * ( 5u - i ) ) );
++x;
} );
}
task.get();
std::cout << x << std::endl;
return 0;
}我不太确定
我无法通过在互联网上阅读文档来回答这个问题,所以我想我应该写上面的片段来找出我们的编译器到底做了些什么。
现在,我发现gcc-5所做的事情的答案是优柔寡断的,这让我更加好奇:人们会认为任务要么是阻塞的,要么是非阻塞的。
如果是阻塞,程序所花费的时间基本上应该是单个任务执行所需时间的总和。第一个是10秒,第二个是8秒,第三个是6秒,第四个是4秒,最后是2秒。因此,总共要花费10+8+6+4+2 =30秒。
如果它是非阻塞的,它应该持续到最后一个任务,即2秒。
下面是发生的情况:需要18秒(使用时间./a.out或一个好的旧时钟来度量)。通过对代码的一些操作,我发现代码的行为就好像分配是交替阻塞的和非阻塞的。
但这不可能是真的,对吧?std::async可能一半时间都会回到std::deferred身上?我的调试器说,它生成两个线程,阻塞直到两个线程退出,然后再生成两个线程等等。
标准怎么说?应该发生什么?gcc-5里面发生了什么?
发布于 2016-07-01 09:12:16
通常,task通过operator=(&&)分配的任务不一定是阻塞的(参见下面),但是由于您使用std::async创建了std::future,这些分配就变成了阻塞(多亏了@T.C.):
[future.async]
如果实现选择了启动::异步策略,
为什么你有18秒的执行时间?
在您的情况下,std::async在分配lambda 之前为lambda启动“线程”--有关如何获得18秒执行时间的详细说明,请参见下文。
这就是(可能)在代码中发生的事情(e代表epsilon):
t = 0,首先用i = 0调用std::async,启动一个新线程;t = 0 + e,第二个std::async调用,i = 1启动一个新线程,然后移动。移动将释放task的当前共享状态,阻塞大约10秒(但是第二个带有i = 1的std::async已经在执行);t = 10,第三个std::async调用,i = 2启动一个新线程,然后移动。task的当前共享状态是对i = 1的调用,该调用已经就绪,所以没有阻塞;t = 10 + e,第四个std::async调用,i = 3启动一个新线程,然后移动。移动是阻塞的,因为以前的std::async与i = 2还没有准备好,但是i = 3的线程已经启动;t = 16,第五个std::async调用,i = 4启动一个新线程,然后移动。task (i = 3)的当前共享状态已经就绪,因此没有阻塞;t = 16 + e,从循环中调用.get(),等待*共享状态准备就绪;t = 18,共享状态已经就绪,所以所有的事情都结束了。关于std::future::operator=的标准细节
以下是operator= on std::future的标准报价
未来&operator=(未来& rhs)除外; 效果:
以下是“释放任何共享状态”的意思(重点是我的):
当异步返回对象或异步提供程序被称为释放其共享状态时,它意味着: (5.1) - ..。 - ..。 -这些操作不会阻止共享状态的就绪,但如果以下所有内容都为真,则可能会阻塞:共享状态是通过调用std::异步创建的,共享状态尚未准备好,这是对共享状态的最后一次引用。
你的情况与我强调的(我认为)是一致的。您使用std::async创建了共享状态,它处于休眠状态(还没有准备好),而且您只有一个对它的引用,因此这个可能会阻塞。
发布于 2016-07-01 09:14:57
保证在打印结果时执行所有任务,
只保证最后分配的任务已被执行。至少,我找不到任何规则来保证剩下的。
任务是否会一个接一个地执行(即任务分配会阻塞)或不执行。
任务分配通常是非阻塞的,但在这种情况下它可能会阻塞-没有保证。
未来]
未来&operator=(未来& rhs)除外;
[futures.state]
- if the return object or provider holds the last reference to its shared state, the shared state is destroyed; and
- the return object or provider gives up its reference to its shared state; and
- these actions will not block for the shared state to become ready, except that **it may block** if all of the following are true: the shared state was created by a call to std::async, the shared state is not yet ready, and this was the last reference to the shared state.
对于std::async创建的尚未执行的任务,所有潜在阻塞的条件都是正确的。
https://stackoverflow.com/questions/38141229
复制相似问题