考虑以下两个代码片段,我试图在其中启动 10000 个线程:
片段1
std::array<std::future<void>, 10000> furArr_;
try
{
size_t index = 0;
for (auto & fut : furArr_)
{
std::cout << "Created thread # " << index++ << std::endl;
fut = std::async(std::launch::async, fun);
}
}
catch (std::system_error & ex)
{
std::string str = ex.what();
std::cout << "Caught : " << str.c_str() << std::endl;
}
// I will call get afterwards, still 10000 threads should be active by now assuming "fun" is time consuming
片段2
std::array<std::thread, 10000> threadArr;
try
{
size_t index = 0;
for (auto & thr : threadArr)
{
std::cout << "Created thread # " << index++ << std::endl;
thr = std::thread(fun);
}
}
catch (std::system_error & ex)
{
std::string str = ex.what();
std::cout << "Caught : " << str.c_str() << std::endl;
}
第一个案例总是成功的。即我可以创建 10000 个线程,然后我必须等待所有线程完成。在第二种情况下,在创建 1600 多个线程后,我几乎总是会遇到异常(“资源不可用,请重试”)。
使用 std::launch::async 的启动策略,我认为这两个片段的行为应该相同。具有 async 启动策略的 std::async 与显式使用 std::thread 启动线程有何不同?
我在Windows 10,VS2015上,二进制文件是在x86发布模式下构建的。
首先,感谢Igor Tandetnik为我提供了这个答案的方向。
当我们使用
std::async
(使用异步启动策略)时,我们会说:
“我想在单独的线程上完成这项工作”。
当我们使用
std::thread
时,我们是在说:
“我想在新线程上完成这项工作”。
细微的差别意味着
async
(通常)使用线程池实现。这意味着,如果我们多次使用 async
调用一个方法,该方法内的线程 ID 通常会重复,即 async
会将多个作业分配给池中的同一组线程。而对于 std::thread
,它永远不会。
这种差异意味着,与使用
async
和 async
启动策略相比,显式启动线程可能会消耗更多资源(因此是例外)。
当我们使用 std::async (带有异步启动策略)时,我们是在说:
“我想在单独的线程上完成这项工作”。
这并非适用于所有实现。
典型的实现实际上会使用线程池
Igor 对典型实现应该做什么的假设是错误的。
std::launch::async
被指定为使得实现可以在最简单的情况下创建新线程。 libstdc++ 和 libc++ 执行此操作。 MSVC++ 使用线程池,但这并不意味着它是一个典型的实现。
看看这些讨论 异步最终会创建新线程 以及这个,尽管它可能与您的查询不直接相关