如何使用C++11 std::thread设置堆叠大小

How to set the stacksize with C++11 std::thread

本文关键字:设置 thread 何使用 C++11 std      更新时间:2023-10-16

我一直在努力熟悉C++11中的std::thread库,但遇到了一个绊脚石。

起初,我来自posix线程背景,想知道如何在构建之前设置std::线程的堆栈大小,因为我似乎找不到任何执行此类任务的参考。

使用pthreads设置堆栈大小如下所示:

void* foo(void* arg);
.
.
.
.
pthread_attr_t attribute;
pthread_t thread;
pthread_attr_init(&attribute);
pthread_attr_setstacksize(&attribute,1024);
pthread_create(&thread,&attribute,foo,0);
pthread_join(thread,0);

使用std::thread时是否有类似的情况?

我一直在使用以下参考:

http://en.cppreference.com/w/cpp/thread

最初我来自posix线程背景,想知道如何在构建之前设置std::线程的堆栈大小,因为我似乎找不到任何执行此类任务的引用。

你不能。std::thread不支持这一点,因为std::thread是标准化的,C++甚至不要求机器有堆栈,更不用说固定大小的堆栈了。

pthreads在其支持的硬件方面更具限制性,并且它假设每个线程有一些固定的堆栈大小。(所以你可以配置这个)

正如Loki Astari已经说过的,实际需要非默认堆栈大小的情况极为罕见,通常是错误或错误编码的结果。

  • 如果你觉得默认堆栈大小太大,无法满足你的需求,并想减少它,那就别想了。现在每个现代操作系统都使用虚拟内存/按需提交,这意味着在你访问页面之前,内存只是保留的,而不是实际分配的。减少堆栈大小将而不是减少实际内存占用。

  • 由于这种行为,操作系统可以将默认堆栈大小设置为非常大的值。例如,在普通的Debian上,这是8MB(ulimit -s),应该足以满足所有需求。如果你仍然能够达到这个极限,我的第一个想法是你的代码是错误的,所以你应该首先审查它,并将其转移到堆中,将递归函数转换为循环,等等

  • 尽管如此,如果确实需要更改堆栈大小(即增加堆栈大小,因为减少堆栈大小是无用的),那么在POSIX上,您可以在程序开始时使用setrlimit来增加默认堆栈大小。当然,这会影响所有线程,但只有那些需要它的线程才会实际使用额外的内存。

  • 最后但同样重要的是,公平地说,我可以看到一个角落的情况,减少堆栈大小是有意义的:如果你在32位系统上有大量线程,它们可能会占用你的虚拟地址空间(同样,不是实际的内存消耗),直到你没有足够的地址空间用于堆。同样,setrlimit是你的朋友,尽管我建议你改用64位系统,以从更大的虚拟地址空间中受益(如果你的程序有那么大,你可能也会从额外的RAM中受益)。

我也一直在调查这个问题。对于某些应用程序,默认堆栈大小不够。示例:该程序根据所解决的特定问题进行深度递归;程序需要创建许多线程,内存消耗是个问题。

以下是我发现的(部分)解决方案/解决方案的摘要:

  • g++支持Linux上的-fsplit-stack选项。有关拆分堆栈的详细信息,请参阅。以下是他们网站上的摘要:

拆分堆栈的目标是允许不连续的堆栈根据需要自动增长。这意味着您可以运行多个线程,每个线程从一个小堆栈开始,并使堆栈增长根据程序要求进行收缩。

备注:-fsplit-stack只有在我开始使用黄金链接器后才对我有效。看来clang++也会支持这面旗帜。我尝试的版本(clang++3.3)在尝试使用标志-fsplit-stack编译应用程序时崩溃。

  • 在Linux上,在启动应用程序之前,通过执行ulimit -s <size>来设置堆栈大小。size是以Kbs为单位的堆栈大小。备注:命令unlimit -s unlimited不会影响使用std::thread创建的线程的大小。当我使用ulimit -s unlimited时,主线程可以增长,但使用std::thread创建的线程具有默认大小。

  • 在使用Visual Studio的Windows上,我们可以在模块定义文件中使用链接器/STACK参数或/STACKSIZE,这是所有创建的线程的默认大小。有关详细信息,请参阅此链接。我们还可以使用命令行工具EDITBIN在任何可执行文件中修改此参数。

  • 在使用mingw g++的Windows上,我们可以使用选项-Wl,--stack,<size>。出于某种原因,当使用cygwin g++时,此标志只会影响主线程的大小。

做过的方法对我不起作用:

  • OSX上的ulimit -s <size>。它只影响主线程的大小。此外,pthread堆栈大小的Mac OSX默认值为512kB。

  • setrlimit只影响Linux和OSX上主线程的大小。在cygwin上,它从未对我起过作用,似乎总是返回一个错误。

对于OSX,唯一的选择似乎是使用boost::thread而不是std::thread,但如果我们想坚持标准,这并不好。我希望g++和clang++将来也能在OSX上支持-fsplit-stack

我在Scott Meyers的《Overview of the New C++(C++0x)》一书中发现了这一点,因为时间很长,我无法将其作为评论发布,这有帮助吗?

还有一个标准的API用于获取特定于平台的线程、互斥对象、条件变量等后面的句柄句柄被假定为用于设置线程优先级的机制,设置堆栈大小等。(关于设置堆栈大小,AnthonyWilliams指出:"在那些支持设置堆栈大小的操作系统中,他们的做法各不相同。如果您正在为指定的平台进行编码(这样使用native_handle就可以了),然后可以使用该平台的交换堆栈设施。例如,在POSIX上,您可以使用makecontext和swapcontext以及显式分配堆栈,在Windows上可以使用Fibers。然后您可以使用用于设置默认值的特定于平台的设施(例如Linker标志)堆栈大小设置为非常小的值,然后将堆栈切换为在必要的地方更大的东西。")

刚才我自己也在寻找答案。

看起来std::thread不支持这个,boost::thread。

特别是,您可以使用boost::thread::attributes来实现这一点:

boost::thread::attributes attrs;
attrs.set_stack_size(4096*10);
boost::thread myThread(attrs, fooFunction, 42);

如果您不想包含一个大库,可以进行一些类似的修改。

它仍然依赖于C++编译器STL库。(Clang/MSVC现在)

破解STL库

std::thread thread = std::stacking_thread(65536, []{
printf("Hello, world!n"); 
});