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::async
和std::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::packaged_task 在 std::async 调用中执行
在 Visual Studio 中,与 std::async 一起使用时未调用“thread_local”变量的析构函数,这是一个错误吗?