Сделать переменную, которая инициализируется функцией, доступной для функции в многопоточной среде

Так вот проблема, которую я пытаюсь решить, я программирую на 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;

, в том числе на уровне файла.

Обновить:

Если вам нужно присвоить значение вашей переменной области файла или статической длительности, которое не является константой времени компиляции (в том числе, если оно имеет агрегатный тип и значение, требуемое для одного из его членов, не является константой времени компиляции), тогда вы не можете использовать инициализатор для этой цели. Вместо этого вы должны договориться о назначении требуемого значения этой переменной после запуска программы, возможно, путем вызова какой-либо функции инициализации.

Другие вопросы по тегам