首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当重新分配到尚未准备好的未来时会发生什么?

当重新分配到尚未准备好的未来时会发生什么?
EN

Stack Overflow用户
提问于 2016-07-01 08:58:55
回答 2查看 1.8K关注 0票数 20

在一次代码评审中,我遇到了一段代码,基本上可以归结为:

代码语言:javascript
复制
#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里面发生了什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-07-01 09:12:16

通常,task通过operator=(&&)分配的任务不一定是阻塞的(参见下面),但是由于您使用std::async创建了std::future,这些分配就变成了阻塞(多亏了@T.C.):

[future.async]

如果实现选择了启动::异步策略,

  • ..。
  • 关联的线程完成与(intro.multithread)同步,该返回来自成功检测共享状态就绪状态的第一个函数,或与同步释放共享状态的最后一个函数的返回,以先发生者为准。

为什么你有18秒的执行时间?

在您的情况下,std::async在分配lambda 之前为lambda启动“线程”--有关如何获得18秒执行时间的详细说明,请参见下文。

这就是(可能)在代码中发生的事情(e代表epsilon):

  • t = 0,首先用i = 0调用std::async,启动一个新线程;
  • t = 0 + e,第二个std::async调用,i = 1启动一个新线程,然后移动。移动将释放task的当前共享状态,阻塞大约10秒(但是第二个带有i = 1std::async已经在执行);
  • t = 10,第三个std::async调用,i = 2启动一个新线程,然后移动。task的当前共享状态是对i = 1的调用,该调用已经就绪,所以没有阻塞;
  • t = 10 + e,第四个std::async调用,i = 3启动一个新线程,然后移动。移动是阻塞的,因为以前的std::asynci = 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)除外; 效果:

  • (10.1) -释放任何共享状态(30.6.4)。
  • ..。

以下是“释放任何共享状态”的意思(重点是我的):

当异步返回对象或异步提供程序被称为释放其共享状态时,它意味着: (5.1) - ..。 - ..。 -这些操作不会阻止共享状态的就绪,但如果以下所有内容都为真,则可能会阻塞:共享状态是通过调用std::异步创建的,共享状态尚未准备好,这是对共享状态的最后一次引用。

你的情况与我强调的(我认为)是一致的。您使用std::async创建了共享状态,它处于休眠状态(还没有准备好),而且您只有一个对它的引用,因此这个可能会阻塞。

票数 13
EN

Stack Overflow用户

发布于 2016-07-01 09:14:57

保证在打印结果时执行所有任务,

只保证最后分配的任务已被执行。至少,我找不到任何规则来保证剩下的。

任务是否会一个接一个地执行(即任务分配会阻塞)或不执行。

任务分配通常是非阻塞的,但在这种情况下它可能会阻塞-没有保证。

未来]

未来&operator=(未来& rhs)除外;

  1. 效果: 释放任何共享状态(futures.state)。

[futures.state]

  1. 当异步返回对象或异步提供程序被称为释放其共享状态时,它意味着:
代码语言:javascript
复制
- 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创建的尚未执行的任务,所有潜在阻塞的条件都是正确的。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38141229

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档