глобальные переменные, повторный ввод кода и безопасность потоков - PullRequest
3 голосов
/ 10 января 2010

Я пытаюсь решить, выполнять ли определенные операции как макросы или как функции.

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

extern int x_GLOB;
extern int y_GLOB;

#define min(x,y) ((x_GLOB = (x)) < (y_GLOB = (y))? x_GLOB : y_GLOB)

цель состоит в том, чтобы каждый аргумент вычислялся только один раз (min(x++,y++) не вызовет здесь никаких проблем).

Вопрос в том, представляют ли две глобальные переменные какую-либо проблему с точки зрения повторного входа в код или безопасности потоков?

Я бы сказал нет, но я не уверен.

А как насчет:

#define min(x,y) ((x_GLOB = (x)), \
                  (y_GLOB = (y)), \
                  ((x_GLOB < y_GLOB) ? x_GLOB : y_GLOB)

это будет другой случай?

Обратите внимание, что вопрос не «в общем», а связан с тем, что эти глобальные переменные используются только внутри этого макроса и для оценки одного выражения.

В любом случае, глядя на ответы ниже, я думаю, что могу подвести итог следующим образом:

  • Это не потокобезопасно, так как ничто не гарантирует приостановку потока "в середине" вычисления выражения (как я и надеялся вместо этого)

  • «Состояние», которое представляют эти глобальные переменные, является, по крайней мере, внутренним состоянием операции «min», и сохранение этого состояния, по крайней мере, опять же, потребует наложения ограничений на способ вызова функции (например, избегать мин (мин (1,2), мин (3,1)).

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

Ответы [ 6 ]

4 голосов
/ 10 января 2010

Тихая модификация глобальных переменных внутри макроса типа min - очень плохая идея. Вы намного лучше

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

Если вы делаете что-то из этого, вы полностью исключаете глобальные переменные и, следовательно, любые вопросы о повторном входе и многопоточности.

Вариант 1:

#define MIN(x, y) ((x) < (y) ? (x) : (y))

Вариант 2:

int min(int x, int y) { return x < y ? x : y; }

Конечно, проблема в том, что вы не можете использовать эту версию min для разных типов аргументов.

Вариант 3:

#define min(x, y) ({ \
    typeof(x) x__ = (x); \
    typeof(y) y__ = (y); \
    x__ < y__ ? x__ : y__; \
})
2 голосов
/ 10 января 2010

Глобалы не являются поточно-ориентированными. Вы можете сделать это, используя локальное хранилище потоков .

Лично я бы использовал встроенную функцию вместо макроса, чтобы вообще избежать этой проблемы!

1 голос
/ 10 января 2010

Лично я не думаю, что этот код вообще безопасен, даже если мы не рассматриваем многопоточные сценарии.

Примите следующую оценку:

int a = min(min(1, 2), min(3, 4));

Как это будет правильно расширяться и оцениваться?

  1. Оцените min (1, 2) и присвойте значение x_GLOB (внешний макрос):
    • x_GLOB = 1
    • y_GLOB = 2
    • оцените мин, результат 1, присвойте x_GLOB (для внешнего макроса)
  2. Оцените min (3, 4) и присвойте значение y_GLOB (внешний макрос):
    • x_GLOB = 3 (э-э-э, 1 из первого расширения теперь засорена)
    • y_GLOB = 4
    • оцените мин, результат равен 3, присвойте y_GLOB (для внешнего макроса)
  3. Оцените min (a, b), где a - это min (1, 2), а b - это min (3, 4)
    • оцените мин. X_GLOB (который равен 3) и y_GLOB (который равен 3)
    • результат общей оценки - 3

Или я что-то здесь упускаю?

1 голос
/ 10 января 2010

Это не потокобезопасно. Чтобы продемонстрировать это, предположим, что поток A вызывает min (1,2), а threadB - min (3,4). Предположим, что поток A выполняется первым и прерывается планировщиком прямо у вопросительного знака макроса, поскольку его временной интервал истек.

Тогда x_GLOB равен 1, а y_GLOB равен 2.

Теперь предположим, что threadB работает некоторое время и завершает содержимое макроса:

x_GLOB равно 3, y_GLOB равно 4.

Теперь предположим, что threadA возобновляется. Он вернет x_GLOB (3) вместо правильного ответа (1).

Очевидно, я немного упростил вещи - прерывание "под знаком вопроса" довольно сложное, и threadA не обязательно возвращает 3, в некоторых компиляторах с некоторыми уровнями оптимизации он может хранить значения в регистрах и не читать обратно из x_GLOB вообще. Таким образом, выдаваемый код может работать с этим компилятором и опциями. Но, надеюсь, мой пример имеет значение - вы не можете быть уверены.

Я рекомендую вам написать статическую встроенную функцию. Если вы пытаетесь быть очень портативным, сделайте это так:

#define STATIC_INLINE static

STATIC_INLINE int min_func(int x, int y) { return x < y ? x : y; }

#define min(a,b) min_func((a),(b))

Тогда, если у вас есть проблемы с производительностью на какой-то конкретной платформе, и это происходит из-за того, что компилятору не удается встроить ее, вы можете беспокоиться о том, нужно ли на этом компиляторе определять STATIC_INLINE по-другому (static inline в C99 или static __inline для Microsoft, или что-то еще), или, возможно, даже реализует макрос min по-другому. Этот уровень оптимизации («он встроен?») - это не то, что вы можете сделать с помощью переносимого кода.

1 голос
/ 10 января 2010

Вообще говоря, всякий раз, когда вы используете глобальные переменные, ваша подпрограмма (или макрос) не возвращается.

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

См. Reentrant Википедии страница

0 голосов
/ 10 января 2010

Этот код небезопасен для многопоточности.

Просто избегайте макросов, за исключением случаев, когда это необходимо (книги «Эффективный C ++» содержат примеры таких случаев).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...