Сделать переменную, которая инициализируется функцией, доступной для функции в многопоточной среде
Так вот проблема, которую я пытаюсь решить, я программирую на C.
У нас есть функция, которая может инициализировать структуру для вас.
typedef struct {
int val1;
int val2;
} custom_t;
custom_t init_custom() {
custom_t temp;
temp.val1 = 5;
temp.val2 = 5;
return temp;
}
И вы бы просто использовали это так:
custom_t some_name = init_custom();
У меня есть 4 функции, которые принимают custom_t в качестве входных данных и могут работать с ним.
В другом файле у меня много библиотечных функций, которые будут работать в многопоточной среде. Все эти библиотечные функции должны будут работать с одной и той же переменной custom_t независимо от потока.
Библиотечные функции не получат переменную custom_t, потому что цель состоит в том, чтобы другой пользователь мог использовать библиотечные функции, не думая о переменной custom_t.
Я думаю, что я должен сделать переменную custom_t глобальной в пространстве имен, где я определяю библиотечные функции, но я ошибаюсь, говоря, что глобальные переменные должны быть константными.
Я не уверен, как этого добиться, и я был бы признателен за любую помощь, которую я могу получить. Если мое объяснение не было достаточно хорошим, не стесняйтесь задавать любые вопросы, и я постараюсь уточнить.
РЕДАКТИРОВАТЬ: Исправлена переменная инициализации опечатка
2 ответа
С custom_t = init_custom();
вы пытаетесь установить имя типа (т.е. custom_t
).
Просто назовите это как-нибудь еще:
custom_t my_global_custom = init_custom();
Но, чтобы получить доступ к нему из нескольких потоков и библиотечных функций, предполагая, что вам нужно записать в него, вам нужно обернуть доступ к этому в мьютекс:
pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;
my_global_custom = init_custom();
// how each thread must access it
pthread_mutex_lock(&custom_mutex);
func_that_uses_my_global_custom();
pthread_mutex_unlock(&custom_mutex);
ОБНОВИТЬ:
Мой пример не был буквально инициализатором, а назначением:
pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;
custom_t my_global_2;
custom_t
init_custom(void)
{
custom_t temp;
temp.val1 = 5;
temp.val2 = 5;
return temp;
}
void
init_custom2(custom_t *temp)
{
temp->val1 = 5;
temp->val2 = 5;
}
int
main(void)
{
// either one of these should work ..
my_global_custom = init_custom();
init_custom2(&my_global_2);
// start some threads ...
return 0;
}
void *
thread_func(void *)
{
// how each thread must access it:
pthread_mutex_lock(&custom_mutex);
func_that_uses_my_global_custom();
pthread_mutex_unlock(&custom_mutex);
return (void *) 0;
}
ОБНОВЛЕНИЕ № 2:
Но знаете ли вы какой-либо способ инициализации my_global_custom вне основной функции? Или это просто невозможно?
Другой способ [под gcc
по крайней мере], чтобы создать функцию конструктора. Учитывая приведенные выше функции и определения, переместите вызовы init в:
void __attribute__((constructor))
my_global_constructor(void)
{
my_global_custom = init_custom();
init_custom2(&my_global_2);
}
Ничто не должно [и ничто не должно ] вызывать эту функцию. Он будет вызываться автоматически перед main
вызывается, потому что теперь это специальная функция.
Они часто используются библиотеками, которые хотят выполнить инициализацию, но не хотят обременять main
с необходимостью знать, чтобы позвонить (например) init_liba(); init_libb(); ...
В этом случае он вызывается в "правильное" время для библиотеки [на основе связи и т. Д.].
Также есть __attribute__((destructor))
чем можно использовать для "уничтожения" вещей [после main
возвращается, IIRC].
Подробнее об этом см.: Как именно работает __attribute__((конструктор))?
Лично я теперь использую вышеупомянутый атрибут, но, для ностальгии, мне нравится старшая .init/.fini
разделы.
Как поясняется в вашем комментарии к ответу @CraigEstey, проблема не в том, что переменная должна быть const
, но его инициализатор должен быть константой времени компиляции. Один из подходов к решению этой проблемы состоит в том, чтобы определить статическое значение инициализации с помощью макроса, а не как возвращаемое значение функции. Например, в некотором соответствующем заголовочном файле объявить
typedef struct {
int val1;
int val2;
} custom_t;
#define CUSTOM_T_INITIALIZER { .val1 = 5, .val2 = 5 }
Затем вы можете инициализировать переменные типа custom_t
вот так:
custom_t some_name = CUSTOM_T_INITIALIZER;
, в том числе на уровне файла.
Обновить:
Если вам нужно присвоить значение вашей переменной области файла или статической длительности, которое не является константой времени компиляции (в том числе, если оно имеет агрегатный тип и значение, требуемое для одного из его членов, не является константой времени компиляции), тогда вы не можете использовать инициализатор для этой цели. Вместо этого вы должны договориться о назначении требуемого значения этой переменной после запуска программы, возможно, путем вызова какой-либо функции инициализации.