std::async 不会立即调用

Posted

技术标签:

【中文标题】std::async 不会立即调用【英文标题】:std::async is not called immediately 【发布时间】:2018-08-18 13:46:01 【问题描述】:

我有一些代码示例。我将launch::async 传递给async,所以我希望lambda 函数会被立即调用,但它不会。

#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <chrono>
#include <string>

using namespace std;

mutex m;

void print(string s)

    lock_guard<mutex> l(m);
    cout << s << endl;


int main() 
    print("main thread1");

    std::future<int> f = std::async(launch::async, []()
        print("async");
        return 1;
    );

    print("main thread2");
    int i = f.get();
    print("main thread3");

    return 0;

我期望的结果如下:

main thread1
async
main thread2
main thread3

但真正的输出是这样的:

main thread1
main thread2
async
main thread3

你能解释一下为什么只有当future的get()被调用时才会调用lambda? 如果我将sleep_for 放在main thread2 之前,输出就是我所期望的。

【问题讨论】:

lambda 被异步调用。它可以在调用std::asyncstd::future::get 之间的main 的任何部分之前、之后或同时调用。 【参考方案1】:

只要新线程创建,对async 的调用就会返回。您现在有两个单独的线程,这两个线程中的操作之间的唯一关联是调用async 创建的线程必须在调用f.get() 返回之前完成。所以你看到的行为是符合要求的。您期望的行为也符合要求。这就是运行单独线程的意义所在:它们独立运行,直到您对它们进行同步操作。

【讨论】:

所以如果我理解 lambda 被尽快调用并且稍后返回数据是从第二个线程获取的?那么“只有在调用get() 时才会启动lambda”这句话是不对的? 只要线程调度程序处理它,就会调用线程函数。如果在调用get() 时线程尚未完成(通过调用并从线程函数返回),则对get() 的调用会阻塞,直到线程完成。【参考方案2】:

您的期望表明您并不真正了解异步的含义。我们来看看下面的算法:

operation1
asynchronous operation2
operation3

这里 operation1 在异步调用 operation2 之前被执行。当异步调用 operation2 时,会创建一个新线程并且不等待它的结束。如果 operation3 期望 operation2 已经执行,那么这是代码中的错误。如果您需要给定线程的结果,则需要与之同步。

【讨论】:

感谢您的回答。是的,我明白你的意思。那么为什么当我调试代码并在get() 处设置断点并在执行此行期间开始执行lambda 时呢? get() 不应该只获取数据而不是开始执行 lambda 吗? @drewpol 因为异步打开的线程是在您查看代码时执行的。我们人类对于这种事情来说太慢了。【参考方案3】:

试试这个:

#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <chrono>
#include <string>

using namespace std;
using namespace std::chrono;

mutex m;

void print(string s)

    lock_guard<mutex> l(m);
    nanoseconds ms = duration_cast< nanoseconds >(
        system_clock::now().time_since_epoch()
    );
    cout << s << "(" << ms.count() << ")" << endl;


int main() 
    print("main thread1");

    future<int> f = async(launch::async, []()
        print("async");
        return 1;
    );

    //this_thread::sleep_for(2s);
    print("main thread2");
    int i = f.get();
    print("main thread3");

    return 0;

尝试使用睡眠功能 (this_thread::sleep_for(2s);) 以了解发生了什么。

如果您想在“主线程 2”之前看到“异步” - 在 print 函数调用之前设置此睡眠。

在每个平台和操作系统中创建线程都是一项非常艰巨的任务,它需要很多纳秒才能完成,而仅仅输出一些东西则要快得多。因此,您甚至可以再添加 10 个 print 调用 - 它们会比在新线程中打印更快。

但是您的代码中的f.get() 会强制系统等待结果。它就像睡眠一样工作。因此,这就是为什么在您的代码示例中最后打印“主线程 3”的唯一原因。

我用这段代码得到输出:

./a.out 
main thread1(1534603161902827214)
main thread2(1534603161902924375)
async(1534603161902977095)
main thread3(1534603161903114658)

'主线程1'和'主线程2'之间的区别是97161。 '主线程2'和'主线程3'之间的差异是190283。所有输出都在同一个线程中,一步一步。但是这个f.get() 会阻止最后一次调用。

还有一点。 C++ 是一种非常难的语言,迂腐的语法是你对抗代码错误的帮手。您在这里使用using namespace std;,但无论如何都要写std:: 命名空间。这是风格上的错误。

【讨论】:

以上是关于std::async 不会立即调用的主要内容,如果未能解决你的问题,请参考以下文章

修复这个 std::async 调用

并非所有 std::packaged_task 在 std::async 调用中执行

在 Visual Studio 中,与 std::async 一起使用时未调用“thread_local”变量的析构函数,这是一个错误吗?

C++ 从 std::async 函数读取命名空间中的全局变量标志

C++ std::async 不会产生新线程

如何检查 std::async 任务是不是完成?