异步是否总是在C++中使用另一个线程/内核/进程
Does async always use another thread/core/process in C++?
据我所知,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
.这使得 main 看起来不会等待async
完成。
据我所知,异步在另一个线程/进程/核心中执行一个函数并且不会阻塞主线程,但它总是发生吗?
仅当std::launch::async
作为第一个参数传递时,std::async
才能保证在单独的线程上执行:
std::launch::async
:启动一个新线程以异步执行任务std::launch::deferred
第一次请求任务结果时在调用线程上执行任务(延迟求值)
默认启动策略为std::launch::async | std::launch::deferred
。
std::async
返回std::future
.std::future
的析构函数只有在从std::async
返回未来时才会阻塞:
这些操作不会阻止共享状态变为就绪状态,但如果满足以下所有条件,则可能会阻止:共享状态是通过调用 std::async 创建的,共享状态尚未就绪,这是对共享状态的最后一次引用
-
在你的第一个代码片段中,你创建一个立即被销毁的右值表达式- 因此
"async"
将在"main"
之前打印。-
异步匿名函数被创建并开始执行。
-
异步匿名函数被销毁。
-
main
执行被阻止,直到函数完成。 -
打印
"async"
。
-
-
main
执行恢复。- 打印
"main"
。
- 打印
-
-
在第二个代码段中,创建一个左值表达式,其生存期绑定到变量
f
。f
将在main
函数的作用域结束时销毁- 因此"main"
由于Delay(1000)
,将在"async"
之前打印。-
异步匿名函数被创建并开始执行。
- 有一种
Delay(1000)
会延迟"async"
立即打印。
- 有一种
-
main
执行将继续。- 打印
"main"
。
- 打印
-
main
的范围结束。 -
异步匿名函数被销毁。
-
main
执行将被阻止,直到函数完成。 -
打印
"async"
。
-
-
它打印
async main
,那么这是否意味着主线程会等到async
完成?
是的,确实如此,但那是因为您没有从async
捕获返回的未来。async
的特殊之处在于,从它返回的future
在析构函数中阻塞,直到线程完成。 由于您没有捕获返回的future
async(launch::async,[]()
{
Sleep(1000);
puts("async");
});
必须在当前线程中取得进展之前完成,因为返回的future
在表达式末尾被销毁。
它打印
main async
.这使得 main 看起来不会等待async
完成。
当你打电话给async
时,这才是你真正想要的。 由于您已经捕获了未来,因此在异步任务完成时,您的主线程可以继续。 由于该线程有延迟,因此main
将在线程之前打印。
如果传递std::launch::async
,则std::async
必须像在自己的线程中运行一样运行任务。
C++线程的唯一概念是std::thread
。
std::async
返回具有唯一属性的std::future
;如果销毁,它将阻止存储在std::async
中的任务完成。 当您无法捕获返回值时,这会捕获您;返回的std::future
是一个未命名的临时存在,并在"该行的尽头"被销毁。
此销毁等待async
任务完成。
在您存储它的情况下,此延迟会等到变量f
被销毁,即在main
的末尾,也就是我们打印之后。
请注意,至少 C++11 的一个主要实现 MSVC 2015 和 2017 最多具有使用线程池而不是新线程的勉强兼容std::async
。 此线程池意味着一组长时间运行的async
调用可能会使其他async
调用无法运行。
使用线程池是合法的(只要它重新创建任何线程局部),但如果所有现有线程都忙于"太长时间",它应该尽量避免饥饿并创建新线程。
它勉强兼容,因为该标准仅指出线程"应该"向前推进。 由于随机原因而从不进展的线程在C++下是合法的;从某种意义上说,你可以争辩说,这就是std::async
在这些情况下效仿的,从而通过了假设测试。
这是因为std::future
的析构函数(从std::async
返回)等待其任务完成。
在第一个代码片段中,从std::async
返回的临时std::future
对象在语句末尾被销毁,因为如 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::async 移出或绑定 对于引用,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
在《有效的现代C++》一书的第38项中,这表示为:
最后一个未来的析构函数引用 通过 std::async 块启动的非延迟任务,直到任务 完成。从本质上讲,这种未来的析构函数确实 异步执行任务的线程上的隐式联接 正在运行。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在另一个线程中调用luaL_error会引发qWarning
- 最佳做法是从另一个线程访问 qml 中的Q_PROPERTY
- C++线程:如何在一个线程仍在运行时阻止另一个线程执行 (Win32)
- 结束另一个线程中使用的对象的生存期
- 从不同进程中的另一个线程挂起/恢复线程或进程
- 两个线程一个使用流 Api,另一个线程创建文件失败并出现错误ERROR_SHARING_VIOLATION
- 计时器是否从另一个线程启动?
- 互斥,Windows 10,c ++,在一个线程上获取,在另一个线程上发布
- Qt 在另一个线程中无限循环
- 在销毁期间从另一个线程调用对象上调用方法是否未定义行为?
- 从另一个线程发出信号是否安全?
- AMQP-CPP,libev >停止ev_loop来自另一个线程
- 在没有任何锁的情况下加入另一个线程后是否需要内存围栏?
- ::grpc::ServerReaderWriter 对象在另一个线程中一段时间后无法调用
- 如何在 qt 中从另一个线程运行 qt并发时关闭程序
- C++将互斥锁锁定为来自另一个线程
- QTcpSocket:消息不是从另一个线程发送的
- 如何从另一个线程调用颤振引擎方法
- 在另一个线程上发出 QObject 信号的正确方法?