对 std::async 使用 std::launch::async 参数启动的线程感到困惑
Confusion about threads launched by std::async with std::launch::async parameter
我对 std::async
函数有点困惑。
规范说:
asynchronous operation being executed "as if in a new thread of execution" (C++11 §30.6.8/11).
现在,那是什么意思?
在我的理解中,代码
std::future<double> fut = std::async(std::launch::async, pow2, num);
应该在新线程上启动函数 pow2
并将变量 num
按值传递给线程,然后在将来某个时候,当函数完成时,将结果放入 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
应该意味着我明确声明,我不想要一个新线程,我只是想确保在需要它的 return 值时调用该函数.
我的假设是否正确?如果不正确,请解释。
此外,我知道默认情况下函数调用如下:
std::future<double> fut = std::async(std::launch::deferred | std::launch::async, pow2, num);
在这种情况下,我被告知是否启动新线程取决于实现。同样,那是什么意思?
std::async
(<future>
header的一部分)函数模板用于启动一个(可能)异步任务。它 return 是一个 std::future
object,它最终将保存 std::async
的参数函数的 return 值。
当需要该值时,我们在 std::future
实例上调用 get();这会阻塞线程,直到未来准备就绪,然后 returns 值。 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.开始,我们可以说总是启动一个新线程。
我在 std::launch::deferred] 上的假设是否正确?
根据2.,可以说你的假设是正确的。
这是什么意思? [关于是否启动新线程取决于实现]
来自3.,因为std::launch::async | std::launch::deferred
是默认选项,也就是说模板函数的实现std::async
将决定是否创建是否有新线程。这是因为某些实现可能正在检查过度调度。
警告
以下部分与您的问题无关,但我认为牢记这一点很重要。
C++ 标准规定,如果 std::future
持有对异步函数调用对应的共享状态的最后引用,则 std::future 的析构函数必须阻塞,直到异步 运行ning 函数完成。由 std::async
编辑的 std::future
return 的实例将因此阻塞其析构函数。
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::async
编辑的 return 实例是临时的并且会阻塞,因为它们的析构函数在 std::async
return 时被调用,因为它们没有分配给变量.
对 std::async
的第一次调用将阻塞 2 秒,随后对 std::async
的第二次调用将阻塞另外 2 秒。我们可能认为最后一次调用 std::async
不会阻塞,因为我们将它的 returned std::future
实例存储在一个变量中,但因为那是一个局部变量,最后会被销毁在范围之外,当局部变量 f 被销毁时,它实际上会在函数范围的末尾再阻塞 2 秒。
换句话说,调用 operation()
函数将阻塞同步调用它的任何线程大约 6 秒。未来版本的 C++ 标准中可能不存在此类要求。
我用来整理这些笔记的信息来源:
C++ 并发实战:实用多线程,Anthony Williams
Scott Meyers 的博客 post:http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html
我也对此感到困惑,运行 对 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
事实并非如此。
添加 thread_local
个存储值,你会看到,实际上 std::async run f1 f2 f3
个任务在不同的线程中,但具有相同的 std::thread::id
我对 std::async
函数有点困惑。
规范说:
asynchronous operation being executed "as if in a new thread of execution" (C++11 §30.6.8/11).
现在,那是什么意思?
在我的理解中,代码
std::future<double> fut = std::async(std::launch::async, pow2, num);
应该在新线程上启动函数 pow2
并将变量 num
按值传递给线程,然后在将来某个时候,当函数完成时,将结果放入 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
应该意味着我明确声明,我不想要一个新线程,我只是想确保在需要它的 return 值时调用该函数.
我的假设是否正确?如果不正确,请解释。
此外,我知道默认情况下函数调用如下:
std::future<double> fut = std::async(std::launch::deferred | std::launch::async, pow2, num);
在这种情况下,我被告知是否启动新线程取决于实现。同样,那是什么意思?
std::async
(<future>
header的一部分)函数模板用于启动一个(可能)异步任务。它 return 是一个 std::future
object,它最终将保存 std::async
的参数函数的 return 值。
当需要该值时,我们在 std::future
实例上调用 get();这会阻塞线程,直到未来准备就绪,然后 returns 值。 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.开始,我们可以说总是启动一个新线程。
我在 std::launch::deferred] 上的假设是否正确?
根据2.,可以说你的假设是正确的。
这是什么意思? [关于是否启动新线程取决于实现]
来自3.,因为std::launch::async | std::launch::deferred
是默认选项,也就是说模板函数的实现std::async
将决定是否创建是否有新线程。这是因为某些实现可能正在检查过度调度。
警告
以下部分与您的问题无关,但我认为牢记这一点很重要。
C++ 标准规定,如果 std::future
持有对异步函数调用对应的共享状态的最后引用,则 std::future 的析构函数必须阻塞,直到异步 运行ning 函数完成。由 std::async
编辑的 std::future
return 的实例将因此阻塞其析构函数。
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::async
编辑的 return 实例是临时的并且会阻塞,因为它们的析构函数在 std::async
return 时被调用,因为它们没有分配给变量.
对 std::async
的第一次调用将阻塞 2 秒,随后对 std::async
的第二次调用将阻塞另外 2 秒。我们可能认为最后一次调用 std::async
不会阻塞,因为我们将它的 returned std::future
实例存储在一个变量中,但因为那是一个局部变量,最后会被销毁在范围之外,当局部变量 f 被销毁时,它实际上会在函数范围的末尾再阻塞 2 秒。
换句话说,调用 operation()
函数将阻塞同步调用它的任何线程大约 6 秒。未来版本的 C++ 标准中可能不存在此类要求。
我用来整理这些笔记的信息来源:
C++ 并发实战:实用多线程,Anthony Williams
Scott Meyers 的博客 post:http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html
我也对此感到困惑,运行 对 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
事实并非如此。
添加 thread_local
个存储值,你会看到,实际上 std::async run f1 f2 f3
个任务在不同的线程中,但具有相同的 std::thread::id