Есть ли хак для установки размера стека потока std::thread?
Мне нужно указать размер стека потока для std::thread. К сожалению, не могу воспользоваться boost::thread. В системе std::thread имплементированы через pthread. Может, можно указать размер стека потока для конкретного файла в опциях компилятора или что то подобное?
Ответы (1 шт):
Если компилятор g++ и в системе используется glibc, то можно изменять дефолтные атрибуты, с которыми создаются pthread-ы.
При чём тут g++
В библиотеке libtdc++ класс std::thread создаёт новые потоки с атрибутами по-умолчанию:
- новый поток создаётся методом
void thread::_M_start_thread(_State_ptr, void (*)()) - этот метод вызывает
__gthread_create- встроенную реализацию многопоточности изlibgcc - функция
__gthread_createсоздаёт поток с атрибутами по-умолчанию:static inline int __gthread_create (__gthread_t *__threadid, void *(*__func) (void*), void *__args) { return __gthrw_(pthread_create) (__threadid, NULL, __func, __args); }
Второй аргумент, который NULL, означает, что поток создаётся с атрибутами по-умолчанию.
При чём тут glibc
В glibc есть расширение pthread_setattr_default_np. Эта функция сохраняет значения атрибутов для последующих pthread_create.
Пример: увеличить размер стека в два раза:
pthread_attr_t attr;
pthread_attr_init(&attr); // инициализируется дефолтными значениями
size_t default_stack_size;
pthread_attr_getstacksize(&attr, &default_stack_size);
std::cout << "default stack size = " << default_stack_size << std::endl;
pthread_attr_setstacksize(&attr, 2 * default_stack_size);
pthread_setattr_default_np(&attr);
Полный пример
В примере создаются потоки, которые умеют печатать свои атрибуты. Сначала создаётся поток с системными атрибутами по-умолчанию, затем размер стека увеличивается в два раза.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* Non-posix extensions */
#endif
#include <pthread.h>
#include <stdlib.h> // EXIT_FAILURE
#include <thread>
#include <iostream>
// Exits if status != 0
static void
check_status(int status, const char *msg);
static void
display_pthread_attr(pthread_attr_t *attr, const char *prefix)
{
int s, i;
size_t v;
void *stkaddr;
struct sched_param sp;
s = pthread_attr_getdetachstate(attr, &i);
check_status(s, "pthread_attr_getdetachstate");
std::cout << prefix << "Detach state = " << ((i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" : (i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE"
: "???")
<< std::endl;
s = pthread_attr_getscope(attr, &i);
check_status(s, "pthread_attr_getscope");
std::cout << prefix << "Scope = " << ((i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" : (i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS"
: "???")
<< std::endl;
s = pthread_attr_getinheritsched(attr, &i);
check_status(s, "pthread_attr_getinheritsched");
std::cout << prefix << "Inherit scheduler = " << ((i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" : (i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED"
: "???")
<< std::endl;
s = pthread_attr_getschedpolicy(attr, &i);
check_status(s, "pthread_attr_getschedpolicy");
std::cout << prefix << "Scheduling policy = " << ((i == SCHED_OTHER) ? "SCHED_OTHER" : (i == SCHED_FIFO) ? "SCHED_FIFO"
: (i == SCHED_RR) ? "SCHED_RR"
: "???")
<< std::endl;
s = pthread_attr_getschedparam(attr, &sp);
check_status(s, "pthread_attr_getschedparam");
std::cout << prefix << "Scheduling priority = " << sp.sched_priority << std::endl;
s = pthread_attr_getguardsize(attr, &v);
check_status(s, "pthread_attr_getguardsize");
std::cout << prefix << "Guard size = " << v << " bytes" << std::endl;
s = pthread_attr_getstack(attr, &stkaddr, &v);
check_status(s, "pthread_attr_getstack");
std::cout << prefix << "Stack address = " << stkaddr << std::endl;
std::cout << prefix << "Stack size = " << v << std::endl;
}
static void
thread_fn(const std::string name)
{
int s;
pthread_attr_t gattr;
// pthread_getattr_np() is a non-standard GNU extension that
// retrieves the attributes of the thread specified in its
// first argument.
s = pthread_getattr_np(pthread_self(), &gattr);
check_status(s, "pthread_getattr_np");
std::cout << "Thread '" << name << "' attributes:\n";
display_pthread_attr(&gattr, "\t");
return;
}
int main(int argc, char *argv[])
{
std::thread th1{thread_fn, "Before doubling stack size"};
th1.join();
pthread_attr_t attr;
pthread_attr_init(&attr);
size_t default_stack_size;
auto s = pthread_attr_getstacksize(&attr, &default_stack_size);
check_status(s, "default stack size");
std::cout << "default stack size = " << default_stack_size << std::endl;
s = pthread_attr_setstacksize(&attr, 2 * default_stack_size);
check_status(s, "pthread_attr_setstacksize");
// The pthread_setattr_default_np() is GNU extension of pthreads
// This function sets the default
// attributes used for creation of a new thread—that is, the
// attributes that are used when pthread_create(3) is called with a
// second argument that is NULL.
s = pthread_setattr_default_np(&attr);
check_status(s, "pthread_setattr_default_np");
std::thread th2{thread_fn, "After doubling stack size"};
th2.join();
}
static void
check_status(int status, const char *msg)
{
if (status != 0)
{
std::cerr << msg << ": status : " << status << std::endl;
std::exit(EXIT_FAILURE);
}
}
Результат работы в Ubuntu-22:
Thread 'Before doubling stack size' attributes:
Detach state = PTHREAD_CREATE_JOINABLE
Scope = PTHREAD_SCOPE_SYSTEM
Inherit scheduler = PTHREAD_INHERIT_SCHED
Scheduling policy = SCHED_OTHER
Scheduling priority = 0
Guard size = 4096 bytes
Stack address = 0x7f61d5af8000
Stack size = 8388608
default stack size = 8388608
Thread 'After doubling stack size' attributes:
Detach state = PTHREAD_CREATE_JOINABLE
Scope = PTHREAD_SCOPE_SYSTEM
Inherit scheduler = PTHREAD_INHERIT_SCHED
Scheduling policy = SCHED_OTHER
Scheduling priority = 0
Guard size = 4096 bytes
Stack address = 0x7f61d4af7000
Stack size = 16777216
Видно, что размер стека во втором потоке увеличился в два раза по сравнению с первым.