我对std::async
函数感到有些困惑。
规范说:正在执行异步操作“就像在新的执行线程中一样”(C ++11§30.6.8/ 11)。
现在,这意味着什么?
根据我的理解,代码
std::future<double> fut = std::async(std::launch::async, pow2, num);
应该在新线程上启动函数pow2
并按值传递变量num
,然后在将来的某个时候,当函数完成时,将结果放在{{1 (只要函数fut
具有类似pow2
的签名)。但规范说“似乎”,这使整个事情对我来说有点模糊。
问题是:
在这种情况下是否始终会启动新主题?我希望如此。我的意思是对我来说,参数double pow2(double);
是有道理的,我明确说明我确实要创建一个新线程。
代码
std::launch::async
通过将std::future<double> fut = std::async(std::launch::deferred, pow2, num);
函数调用延迟到我写pow2
之类的点,应该使延迟评估成为可能。在这种情况下,参数var = fut.get();
应该意味着我明确说明,我不想要一个新线程,我只是想确保在需要它的返回值时调用该函数。
我的假设是否正确?如果没有,请解释。
另外,我知道默认情况下该函数调用如下:
std::launch::deferred
在这种情况下,我被告知是否会启动新线程取决于实现。再一次,这意味着什么?
答案 0 :(得分:40)
std::async
(<future>
标头的一部分)功能模板用于启动(可能)异步任务。它返回一个std::future
对象,最终将保存std::async
参数函数的返回值。
当需要该值时,我们在std::future
实例上调用get();这将阻塞线程,直到将来准备就绪,然后返回值。可以将std::launch::async
或std::launch::deferred
指定为std::async
的第一个参数,以指定任务的运行方式。
std::launch::async
表示函数调用必须在其自己的(新)线程上运行。 (将用户@ T.C.的评论考虑在内)。std::launch::deferred
表示函数调用将被推迟,直到将来调用wait()
或get()
为止。在此之前,未来的所有权可以转移到另一个线程。std::launch::async | std::launch::deferred
表示实施可以选择。这是默认选项(如果您自己未指定)。它可以决定同步运行。在这种情况下是否始终启动新主题?
从 1。,我们可以说始终会启动新主题。
我的假设[on std :: launch :: deferred]是否正确?
从 2。,我们可以说您的假设是正确的。
这是什么意思? [关于正在启动的新线程,取决于实现]
从 3。,因为std::launch::async | std::launch::deferred
是默认选项,这意味着模板函数std::async
的实现将决定它是否会创建新线程。这是因为某些实现可能会检查过度调度。
警告强>
以下部分与您的问题无关,但我认为请务必记住这一点。
C ++标准规定,如果std::future
保存对与异步函数调用相对应的共享状态的最后一个引用,则std :: future的析构函数必须阻塞,直到异步运行函数的线程结束。因此std::future
返回的std::async
实例将在其析构函数中阻塞。
void operation()
{
auto func = [] { std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); };
std::async( std::launch::async, func );
std::async( std::launch::async, func );
std::future<void> f{ std::async( std::launch::async, func ) };
}
这种误导性代码可能会让您认为std::async
调用是异步的,它们实际上是同步的。 std::future
返回的std::async
个实例是临时的,并且会阻塞,因为在std::async
返回时它们的析构函数被调用,因为它们未被赋值给变量。
对std::async
的第一次呼叫将阻止2秒,然后在第二次呼叫std::async
时再阻止2秒。我们可能认为对std::async
的最后一次调用没有阻塞,因为我们将它返回的std::future
实例存储在一个变量中,但由于这是一个在范围末尾被销毁的局部变量,所以当局部变量f被破坏时,实际上会在函数范围结束时再阻塞2秒。
换句话说,调用operation()
函数将阻塞同步调用的任何线程大约6秒。未来版本的C ++标准可能不存在这些要求。
我用来编辑这些笔记的信息来源:
C ++并发行动:实用多线程,Anthony Williams
Scott Meyers的博文:http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html
答案 1 :(得分:0)
我也对此感到困惑,并在Windows上进行了快速测试,显示异步未来将在OS线程池线程上运行。一个简单的应用程序可以证明这一点,在Visual Studio中突破也将显示名为“TppWorkerThread”的执行线程。
#include <future>
#include <thread>
#include <iostream>
using namespace std;
int main()
{
cout << "main thread id " << this_thread::get_id() << endl;
future<int> f1 = async(launch::async, [](){
cout << "future run on thread " << this_thread::get_id() << endl;
return 1;
});
f1.get();
future<int> f2 = async(launch::async, [](){
cout << "future run on thread " << this_thread::get_id() << endl;
return 1;
});
f2.get();
future<int> f3 = async(launch::async, [](){
cout << "future run on thread " << this_thread::get_id() << endl;
return 1;
});
f3.get();
cin.ignore();
return 0;
}
将导致输出类似于:
main thread id 4164
future run on thread 4188
future run on thread 4188
future run on thread 4188
答案 2 :(得分:0)
事实并非如此。
添加thread_local
存储值,您会看到,实际上std::async run f1 f2 f3
个任务位于不同的线程中,但具有相同的std::thread::id