Статические переменные и потоки (C)

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

вот последний экзамен ЕГЭ вопрос я пытаюсь ответить:

следующая функция C предназначена для использования для выделения уникальных идентификаторы (UIDs) для вызывающих абонентов:

get_uid() 
{
static int i = 0;
return i++;
}

Объясните, каким образом get_uid() может работать неправильно в среде, где он вызывается несколькими потоками. С помощью конкретный пример сценария, дайте конкретную информацию о том, почему и как такое может произойти неправильное поведение.

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

6 ответов


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


все потоки процесса разделяют адресное пространство. С i является статической переменной, имеет фиксированный адрес. Его "состояние" - это просто содержимое памяти по этому адресу, которое совместно используется всеми потоками.

постфикс ++ оператор увеличивает свой аргумент и дает значение аргумента перед увеличением. Порядок их выполнения не определен. Одна из возможных реализаций -

copy i to R1
copy R1 to R2
increment R2
copy R2 to i
return R1

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

такая ситуация, когда существуют разные результаты в зависимости от индетерминированного упорядочения операций в разных потоках, называется гонки условие, потому что есть" гонка " среди разных потоков относительно того, какой из них делает какую операцию в первую очередь.


нет, если вы хотите переменную, значение которой зависит от потока, в котором она используется, Вы должны посмотреть на Локальное Хранилище Потока.

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

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


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


каждый поток будет иметь одну и ту же статическую переменную, которая, скорее всего, является глобальной переменной. Сценарий, в котором некоторые потоки могут иметь неправильное значение, - это условие гонки (инкремент не выполняется за одно выполнение, а выполняется в 3 инструкциях по сборке, загрузке, инкременте, хранилище). Читайте здесь, и диаграмма по ссылке хорошо объясняет это.

Гонки


Если вы используете GCC, вы можете использовать атомное строение функции. Я не уверен, что доступно для других компиляторов.

int get_uid() 
{
    static int i = 0;
    return __atomic_fetch_add(&i, 1, __ATOMIC_SEQ_CST);
}

Это гарантирует, что переменная не может быть выполнена более чем одним потоком одновременно.