什么是 C++ 11 触发异步任务并忘记它的方法?
Posted
技术标签:
【中文标题】什么是 C++ 11 触发异步任务并忘记它的方法?【英文标题】:What's the C++ 11 way to fire off an asynchronous task and forget about it? 【发布时间】:2014-05-04 09:49:38 【问题描述】:我需要这样的东西:
void launch_task()
std::thread([]() run_async_task(); );
除了线程的析构函数会终止我的任务。我不需要对任务进行任何控制,也不需要返回值。它只需要运行它的过程,然后线程应该终止并且 C++ 线程对象应该被处理掉。我需要什么 C++ 11 工具?
我查看了std::async
,但找不到适用于我的案例的使用示例。这似乎是一个相当复杂的系统,我需要以某种方式存储和操作std::future
,否则它会变得同步(如果我的理解是正确的;我在std::async
上没有找到清晰的好文章)。
【问题讨论】:
@Yakk:不在乎。它应该与它正在做的任何事情一起被终止,这在我的应用程序中非常好。 那么,因为main
先退出,你可以格式化你的硬盘吗?对我来说似乎不明智!我更喜欢自己避免鼻恶魔。避免未定义的行为比忽略更好...... C++ 标准对于main
末尾的分离线程会发生什么含糊不清,在某种程度上我个人并不满意。如果您针对的是单一平台,则可以更有信心。
@Yakk:你把 UB 妖魔化了,超出了常识。
假设线程在服务器或该系统的硬件上获取了远程资源,它将通过 RAII 释放。您的“我不在乎”实际上可能会泄漏。如果运气不好,标准要求 no 保证会发生什么:如果您正在为一种架构编程并且知道代码不会用于任何其他目的(丢弃代码),您可以确定它们实际上是如何处理它,最坏的情况是得到上述结果。 *(*char)0=7
也是一种通常安全的终止方式。
@Yakk:我不是在写库。我正在编写一个应用程序,并且确切地知道将在这个线程中执行什么。所以我知道终止或放弃它是安全的。一般来说 - 是的,你当然是对的。这将是草率的编程。
【参考方案1】:
创建后立即将其分离。
std::thread([]() run_async_task(); ).detach();
一旦分离,线程将不再可连接,因此~thread()
将无效。
This answer 讨论了此行为的更多细节。
正如 W.B. 所说。下面,std::async
将无法工作,原因如下,从这个reference 中提取。
如果从 std::async 获得的 std::future 有临时对象 生命周期(未移动或绑定到变量), std::future 将在完整表达式的末尾阻塞,直到 异步操作完成
【讨论】:
Lambda 在这里是多余的。std::thread(run_async_task).detach();
@JanickBernet 和std::async
你不能只是启动然后忘记它,因为一旦std::future
返回值超出范围,它将阻塞直到异步函数返回。
@JanickBernet 不幸的是std::async
不是很“异步”。它实际上是阻塞呼叫。
@JanickBernet,看看here。 如果从 std::async 获得的 std::future 具有临时对象生存期(未移动或绑定到变量),则 std::future 的析构函数将在完整表达式的末尾阻塞,直到异步操作完成
如果其他人对使用异步阻塞创建未来的原因感兴趣:***.com/a/23460094/369310【参考方案2】:
复活一个旧线程,但是有一个巧妙的技巧*如何通过使用std::async
来实现“即发即弃”功能,尽管它返回了阻塞std::future
。主要成分是指向返回的std::future
的共享指针,它在 lambda 中按值捕获,导致其引用计数器递增。这样,std::future
的析构函数将在 lambda 完成其工作之前不会被调用,从而提供真正的异步行为。
template <class F>
void call_async(F&& fun)
auto futptr = std::make_shared<std::future<void>>();
*futptr = std::async(std::launch::async, [futptr, fun]()
fun();
);
*感谢我的一位同事和真正的 C++ 专家 MVV,他向我展示了这个技巧。
【讨论】:
但它与std::thread....detach()
的作用不一样吗?代价是一些额外的对象、额外的内存分配(用于指针)和额外的 CPU 周期来递增/递减计数器?跨度>
@violetgiraffe 它与 std::thread...detach() 做同样的事情,但我在很多地方读到,用 std::async 实现相同的目标是不可能的,这个答案反驳了。关于性能,Kurt Guntheroth 发现创建线程的成本是使用异步的 14 倍(参见 amazon.com/Optimized-Proven-Techniques-Heightened-Performance/…)。因此,与此相比,少量额外 CPU 周期和额外内存分配的成本可以忽略不计(但应该衡量)。
这种技术将线程管理问题推迟到操作系统,这可能会比您的应用程序做得更好。例如,如果std::async
的实现在后台使用Grand Central Dispatch 或其他一些线程管理系统,您可以在这里免费使用这些功能,而不是为您的N
异步任务启动N
线程,并承担风险超额订阅机器。
这真是一个绝妙的技巧。很好地解释了 lambda 捕获和 shared_ptr 的行为!在找到这个答案之前,我的第一个实现是将未来存储在某种结构中,但是当未来结束时我正在努力解决如何删除它......非常感谢以上是关于什么是 C++ 11 触发异步任务并忘记它的方法?的主要内容,如果未能解决你的问题,请参考以下文章
CompletableFuture异步编排(两任务组合——两个任务必须都完成才触发另一个任务 )