繁体   English   中英

何时使用 std::async 与 std::threads?

[英]When to use std::async vs std::threads?

任何人都可以给出关于何时使用它们的高级直觉吗?

参考:

这不是一个真正的非此即彼的事情 - 您可以将期货(与承诺一起)与手动创建的 std::threads 一起使用。 使用std::async是一种为某些异步计算触发线程并通过 future 将结果编组回的便捷方法,但std::async在当前标准中相当有限。 如果建议的包含来自 Microsoft 的 PPL 的一些想法的扩展被接受,它将变得更加有用。

目前, std::async可能最适合处理相当简单的程序的长时间运行的计算或长时间运行的 IO。 但它并不能保证低开销(事实上,它的指定方式使得很难在后台使用线程池实现),因此它不太适合细粒度的工作负载。 为此,您要么需要使用std::thread滚动自己的线程池,要么使用 Microsoft 的 PPL 或 Intel 的 TBB 之类的东西。

您还可以将std::thread用于以更现代和可移植的方式编写的“传统”POSIX 线程样式代码。

Bartosz Milewski 在他的文章Async Tasks in C++11: Not Pretty There Yet 中讨论了当前指定std::async方式的一些限制

我发现的一个简单原因是,当您想要一种检测(通过轮询)异步作业是否完成的方法时。 使用std::thread ,您必须自己管理它。 使用std::async您可以查询std::future::valid() (或使用std::future::wait_for/wait_until(...) )以了解何时完成。

std::thread上使用std::futrue一个用例是您想要调用一个返回值的函数。 当你想要函数的返回值时,你可以调用未来的get()方法。

std::thread不提供获取函数返回值的直接方法。

我意识到这个问题提出已经 8 年了。 从那时起,C++ 并发环境发生了很大变化。 最近我也不得不在这片风景中徘徊,不知道该走哪条路才能前进。 我想分享我的一些想法,并可能得到验证。 我会将原始问题稍微修改为 std::async 与线程池,而不仅仅是 std::thread。

自 2011 年以来,我一直在大量使用 boost::thread_group 和 boost::asio::io_service 分别用于线程池和事件循环。 我的每个应用程序都是这样开始的:

int noOfCores = boost::thread::hardware_concurrency();
for (int i = 0; i < noOfCores; i++)
{
    _threadPool.create_thread(boost::bind(&pri_queue::run, &_taskQueue));
}

任务队列 _taskQueue 的类型 pri_queue 有点类似于这个boost 示例,除了我的 run() 函数等待 io_service.run_one()。 因此,我还通过在排队时分配优先级来控制任务执行的优先级。

在此之后,我可以使用 post() 在此队列中抛出任何函数(使用 boost::bind 绑定参数)以执行,或者使用 boost::asio::deadline_timer::async_wait() 延迟调度它。

由于我的框架中的所有内容都是事件驱动的,因此我很乐意将任何功能划分为多个函数对象,同时等待诸如异步 http 客户端的 boost 示例中的事件。 这个模型经过了非常长时间的测试,没有线程创建成本,因为每个线程都是预先创建的。

然而,自从我在公司的所有产品中采用这个模型以来,C++ 标准已经更新了 3 次(14、17、20)。 因此,当我看到周围出现的所有新变化时,您可以说我正在遭受一些 FOMO 之苦。 请原谅我,在查看了 std::async 和协程之后,我看不出它们如何帮助像我这样已经习惯使用 io_service + 线程池模型的人。 它看起来更昂贵,而且我无法控制优先级或线程创建,不同编译器的实现不同

我看到它使函数看起来是同步的和结构化的(所有部分都在一个地方),与溢出到多个函数对象中的异步功能相比。

对于 C++ 的老手,我会说线程池比 std::async 甚至协程更好。 当然,如果应用程序不是事件驱动的,或者您不熟悉异步编程,std::async 会更容易处理。

我认为std::async/std::future相对于一般std::thread方法的一个巨大优势是免费的异常传播。 如果您的线程函数抛出, std::future::get将抛出。 这个功能真的很方便。 此功能很安静,类似于处理返回值(由rg665n在不同的答案中说明)。

如果您使用 std::thread ,则需要为您的返回值和可能发生的异常创建一个包含成员的类。 您需要为所有这些成员添加互斥锁/锁。 通常,如果您使用std::thread而不是std::async来完成简单的任务,那么您的代码将像数百行代码一样长。

简而言之,我对您的问题的回答是:如果您的子任务之间没有相互干扰,请使用std::async/std::future 如果您必须在子任务之间同步状态(因为它们相互依赖),请使用std::thread std::thread用于观察者模式或线程,它将运行整个程序生命周期。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM