В частности, я хочу установить значение указателя функции. Для простоты я хочу делать это много раз, из нескольких потоков, но всегда простым способом, например:

typedef void (*F)();
F f = 0;

void foo()
{
}

// called many times from multiple threads
void set()
{
    f = &foo;
}

int main()
{
    set();  // also other threads can invoke it at any time
    f();
    return 0;
}

Таким образом, изначально указатель на функцию равен NULL, а затем становится & foo, когда код выполняется в первый раз. Интересно, может ли из-за какой-либо неатомной операции записи нарушиться указатель функции.

Гарантируется, что он будет прочитан впервые после установки.

РЕДАКТИРОВАТЬ: уточняю:

  1. Основная причина, по которой я использую указатель на функцию, - это удаление некоторых зависимостей между модулями. Это маленький элемент большого реального проекта. Я действительно не могу напрямую вызвать foo.
  2. Я знаю, как программировать, и мне не нужна базовая информация о таких вещах, как мьютекс. У меня вопрос, безопасно ли это БЕЗ мьютекса.
  3. В коде гарантируется, что никакой другой поток не устанавливает указатель ни на что иное, кроме & foo.
2
Michal Czardybon 28 Янв 2015 в 16:40

3 ответа

Лучший ответ

Стандарт говорит, что ваш код вызывает неопределенное поведение. Но в вашем конкретном случае, то есть с вашим конкретным компилятором и архитектурой, ваш код действительно может быть без проблем. Итак, с теоретической точки зрения, ваш код не в порядке. С практической точки зрения ответ зависит от вашего конкретного случая и ни в коем случае не является общим. Лично я предлагаю заменить F f = 0; на std::atomic<F> f(0);, чтобы код гарантированно был в порядке во всех случаях.

Просто чтобы проиллюстрировать случай, когда ваш код ломается: реализация может очистить цель перед каждой операцией записи. Такая реализация законна и соответствует стандарту, хотя и не обязательно отмечена наградами :)

2
Lingxi 28 Янв 2015 в 15:52

Я думаю, что это скорее проблема синхронизации потоков, чем проблема, связанная с C ++. Ответ на ваш вопрос: да, вы можете безопасно записать значение в переменную / указатель на С ++ (не уверен, что вы имеете в виду под константой; мне кажется, что вы просто хотите назначить указатель на функцию ), вам просто нужно использовать взаимное исключение , чтобы потоки не перезаписывали значение, когда вы не хотите, чтобы они

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

0
Pandrei 28 Янв 2015 в 13:48

Рассматривали ли вы возможность использования std::mutex и std::lock_guard для обеспечения безопасности потоков?

Например

{
    std::lock_guard<std::mutex> lg(my_mutex);
    f = &foo;
    // then use f to perform your operation
}

После закрывающей скобки lock_guard выпадает из области видимости и разблокирует ваш mutex. Это означает, что тот, кто устанавливает f, может безопасно использовать этот указатель на функцию, зная, что другой поток не изменил указатель функции между его установкой и попыткой его использования.

4
Cory Kramer 28 Янв 2015 в 13:45