从C ++ 11中的线程返回一个值[重复]
Posted
技术标签:
【中文标题】从C ++ 11中的线程返回一个值[重复]【英文标题】:Return a value from a thread in C++11 [duplicate] 【发布时间】:2014-01-13 02:09:42 【问题描述】:从 C++11 中的线程返回值的最有效方法是什么?
vector<thread> t(6);
for(int i = 0; i < 6; i++)
t[i] = thread(do_c);
for(thread& t_now : t)
t_now.join();
for(int i = 0; i < 6; i++)
cout << /*the return of function do_c*/
另外,如果更改会提高性能,请随时推荐除std::thread
之外的其他线程。
【问题讨论】:
你不小心说了什么?从线程返回一个值,对吧? ?你什么意思?是的 是的,但我想要最有效的方式 @luka - 如果您要启动 100 个线程,则存在性能问题。仅当您有足够的处理器和/或在易于程序实现方面才有意义时,拥有多个线程才有意义。否则你会在上下文切换中失去很多性能。无论如何,加入线程不会有太多的性能开销,因为调用会被操作系统阻止 这里有人不明白io bound和cpu bound的区别 【参考方案1】:首先std::thread
不返回值,但是在构造时传递给它的函数可能会很好地做到这一点。
没有办法从std::thread
对象访问函数的返回值,除非你在线程上调用函数后以某种方式保存它。
一个简单的解决方案,例如将reference传递给线程并将结果存储在引用指向的内存中。使用线程时必须注意不要引入数据竞争。
考虑一个简单的函数:
int func()
return 1;
还有这个例子:
std::atomic<int> x0; // Use std::atomic to prevent data race.
std::thread t[&x] // Simple lambda that captures a reference of x.
x = func(); // Call function and assign return value.
;
/* Do something while thread is running... */
t.join();
std::cout << "Value: " << x << std::endl;
现在,您可以使用标准库,而不是自己处理这些低级并发问题,因为有人(一如既往)已经为您解决了这个问题。 std::packaged_task
和 std::future
旨在与 std::thread
一起解决这种特殊类型的问题。在大多数情况下,它们还应该与自定义解决方案一样高效。
这是一个使用 std::packaged_task
和 std::future
的等效示例:
std::packaged_task<int()> taskfunc; // Create task using func.
auto future = task.get_future(); // Get the future object.
std::thread tstd::move(task); // std::packaged_task is move-only.
/* Do something while thread is running... */
t.join();
std::cout << "Value: " << future.get() << std::endl; // Get result atomically.
不要总是因为某些东西被认为是“高级”而认为它的效率较低。
【讨论】:
我想到的问题是:什么功能要求可以证明使用设计为并行处理大量数据的机制来检索单纯的整数线程返回值?看起来更像我所说的语法驱动的软件设计。这类解决方案很好地展示了 C++11 的多功能性,但它们确实在句法糖的糖霜下隐藏了相当大的资源消耗,恕我直言。 @kuroineko 这完全取决于我猜的域。对于通用应用程序开发,我将使用标准化的类和函数,以便在同事之间实现最大的理解,而不是自定义解决方案。是否有任何功能要求来证明使用抽象是合理的?嗯,我什么都想不出来。但是缺乏功能要求来证明抽象代码的更好可维护性并不会降低它的重要性。非功能性需求也是必不可少的。 在我看来,这是一个在易用性和浪费(即使不是潜在危险的习惯)之间找到最佳平衡点的问题。在这种特定情况下,我认为值得考虑一下舒适的价格。我设计嵌入式软件已经有十年左右的时间了,这可能就是为什么看到投入如此多的资源来避免如此少的努力会让我感到紧张:)。【参考方案2】:启动和终止线程需要数百个机器周期。但这只是一个开始。如果线程正在做任何有用的事情,线程之间的上下文切换必然会发生,它将重复消耗更多数百个机器周期。所有这些线程的执行上下文将消耗许多字节的内存,这反过来又会弄乱许多行缓存,从而阻碍 CPU 的工作再进行数百个机器周期。
事实上,使用多任务处理任何事情都会消耗数百个机器周期。只有当您设法让足够多的处理器处理概念上独立的数据块时,多任务处理才能在CPU 功率使用方面变得有利可图(因此并行处理不会威胁到它们的完整性) 并且大到足以显示与单处理器版本相比的净增益。
在所有其他情况下,多任务处理在所有领域中本质上都是低效的,除了一个:reactivity。任务可以非常快速且准确地对外部事件做出反应,该事件最终来自某些外部硬件组件(无论是定时器的内部时钟还是用于网络流量的 WiFi/以太网控制器)。
这种在不浪费 CPU 的情况下等待外部事件的能力提高了整体 CPU 效率。就是这样。 就其他性能参数(内存消耗、内核调用中浪费的时间等)而言,启动新线程总是会造成净损失。
简而言之,多任务编程的艺术归结为:
识别您必须处理的外部 I/O 流 将反应性要求考虑在内(请记住,99% 的时间,反应性越大 = CPU/内存效率越低) 为所需事件设置处理程序,同时兼顾合理的效率/易于维护。多处理器架构增加了一个新级别的复杂性,因为现在任何程序都可以被视为一个拥有多个外部 CPU 的进程,这些外部 CPU 可以用作额外的电源。但是您的问题似乎与此无关。
多任务处理效率的衡量标准最终将取决于给定程序在给定的一组反应性限制内同时处理的外部事件的数量。
最后我来回答你的具体问题。
为了对外部事件做出反应,每次必须在蚁丘周围移动新的树枝或死昆虫时启动任务是一种非常粗略且低效的方法。
您可以使用许多强大的同步工具,它们可以让您以(几乎)最佳效率且(几乎)免费的情况下从单个任务上下文中对一堆异步事件做出反应。
通常,阻塞等待多个输入,例如 unix 风格的 select()
或 Microsoft 的 WaitForMultipleEvents()
对应项。
使用这些将给你带来无与伦比的性能提升,这比你可以从你的这个任务-结果-收集-优化项目中挤出的几十个 CPU 周期要大得多。
所以我的回答是:根本不用优化线程设置。这不是问题。
您最好将时间花在重新考虑您的架构上,以便少数经过深思熟虑的线程可以取代您当前设计会产生的大量无用 CPU 和内存占用。
【讨论】:
谢谢你的解释,猫先生。这让我改变了主意,决定不使用线程。 @Luka 很高兴为您(以及您的编译器和您的 CPU)免去了很多不必要的痛苦 ;) 抱歉,我错误地对您的帖子投了反对票,请编辑它以支持它...以上是关于从C ++ 11中的线程返回一个值[重复]的主要内容,如果未能解决你的问题,请参考以下文章