据我所知async
在另一个线程/进程/核心中执行一个函数并且不阻塞主线程,但总是这样吗?
我有以下代码:
async(launch::async,[]()
{
Sleep(1000);
puts("async");
});
puts("main");
它打印async main
,这是否意味着主线程等到async
结束?
如果我改为以下:
auto f = async(launch::async,[]() // add "auto f = "
{
Sleep(1000);
puts("async");
});
puts("main");
打印main async
。这使得看起来主要不等待async
完成。
答案 0 :(得分:27)
仅当我知道async在另一个线程/进程/核心中执行一个函数并且不阻塞主线程,但是它总是会发生吗?
std::async
作为第一个参数传递时, std::launch::async
才能保证在单独的线程上执行:
std::launch::async
:启动新线程以异步执行任务std::launch::deferred
第一次请求结果时,在调用线程上执行任务(延迟评估)
默认启动政策为std::launch::async | std::launch::deferred
。
std::async
返回std::future
。 std::future
's destructor仅在未来从std::async
返回时才会阻止:
这些操作不会阻止共享状态变为就绪,除非它可能阻塞以下所有条件都是真的:共享状态是通过调用std :: async创建的,共享状态尚未准备好,这是对共享状态的最后一次引用
在您的第一个代码段中,您创建一个右值表达式 会立即销毁 - 因此"async"
将在"main"
之前打印}。
创建异步匿名函数并开始执行。
异步匿名函数被破坏。
main
执行被阻止,直到功能完成。
"async"
已打印。
main
执行恢复。
"main"
已打印。在第二个代码段中,您创建一个左值表达式,其生命周期绑定到变量f
。 f
将在main
函数范围的末尾销毁 - 因此"main"
会因"async"
而被Delay(1000)
打印}。
创建异步匿名函数并开始执行。
Delay(1000)
延迟"async"
延迟立即打印。 main
执行继续。
"main"
已打印。 main
范围的结束。
异步匿名函数被破坏。
main
执行被阻止,直到功能完成。
"async"
已打印。
答案 1 :(得分:6)
它打印
MyGsonModelClass
,这是否意味着主线程等到async main
结束?
是的,但这是因为您没有从async
捕获返回的未来。 async
的特殊之处在于,从它返回的async
在析构函数中阻塞,直到线程完成。由于您未捕获返回的future
future
必须在当前线程中进行之前完成,因为返回async(launch::async,[]()
{
Sleep(1000);
puts("async");
});
在表达式结束时被销毁。
打印
future
。这使得看起来主要不等待main async
完成。
当您致电async
时,您真正想要的是什么。由于您已捕获了未来,因此在完成异步任务时,允许主线程继续运行。由于你有一个延迟,线程async
将在线程之前打印。
答案 2 :(得分:3)
如果您通过std::launch::async
,则std::async
必须运行任务,就像它在自己的线程中运行一样。
C ++中唯一的线程概念是std::thread
。
std::async
返回具有唯一属性的std::future
;如果被销毁,它会在完成std::async
中存储的任务时阻止。当您未能捕获返回值时,这会陷阱;返回的std::future
是一个未命名的临时存在,它会在该行的第34行结束时被销毁。
此破坏等待async
任务完成。
在您存储它的情况下,此延迟会一直等到变量f
被销毁,这是在main
的末尾,这是在我们打印之后。
请注意,C ++ 11,MSVC 2015和2017的至少一个主要实现具有最佳边际兼容std::async
,它使用线程池而不是新线程。此线程池意味着一组长时间运行的async
调用可能会使其他async
个调用无法运行。
使用线程池是合法的(只要它重新创建任何线程本地),但它应该尽量避免饥饿并在所有现有线程忙于“太长时间”的情况下创建新线程。
它略微合规,因为标准只规定线程"应该"取得进步。由于随机原因而永远不会进展的线程在C ++下是合法的;在某种意义上,你可以说这是std::async
在这些情况下模仿的东西,从而通过了as-if测试。
答案 3 :(得分:0)
这是因为std::future
(从std::async
返回)的析构函数等待其任务完成。
在第一个代码段中,从std::future
返回的临时std::async
对象在语句末尾被销毁,因为用https://en.cppreference.com/w/cpp/language/lifetime编写
所有临时对象都被销毁,这是评估对象的最后一步 完全表达(按词法)包含它们所在的点 已创建
因此,在执行下一条语句之前,std::future
对象的析构函数将阻塞,直到任务完成为止,这意味着puts("async")
在puts("main")
之前执行。
但是,在第二个代码段中,std :: async的返回值被移动到本地对象中,该对象在退出范围时被破坏。因此,按预期,带有async
的行将无阻塞地执行,并且puts("main")
会在puts("async")
(被Sleep
调用阻塞)之前执行。
此行为在https://en.cppreference.com/w/cpp/thread/async中解释为:
如果从std :: async获得的std :: future没有移出或绑定 引用时,std :: future的析构函数将在 完整表达式的结尾,直到异步操作完成为止, 本质上使代码如下同步:
std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f()
std::async(std::launch::async, []{ g(); }); // does not start until f() completes
在书Effective Modern C++的项目38 中,表示为:
最后一个析构函数的析构函数,指的是 通过std :: async块启动的非延迟任务,直到该任务 完成。本质上,这种未来的破坏者会 在异步执行任务的线程上进行隐式连接 正在运行。