什么是 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 触发异步任务并忘记它的方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个分配不触发?

在异步任务中运行各种命令

javascript异步回调

CompletableFuture异步编排(两任务组合——两个任务必须都完成才触发另一个任务 )

CompletableFuture异步编排(两任务组合——两个任务必须都完成才触发另一个任务 )

CompletableFuture异步编排(两任务组合——两个任务必须都完成才触发另一个任务 )