Не является ли проблема, связанная с повторной регистрацией, возникает только тогда, когда у нас есть несколько потоков?

Я изучал старую концепцию написания реентерабельного кода. Говорят, не используйте глобальные статические переменные. Потому что он склонен к недетерминированному поведению. Тем не менее, я не уверен, применимо ли это там, где есть один поток. Я понимаю, что потокобезопасность и повторный вход — это две разные концепции. При использовании C ++ мы избегаем использования глобальных и статических переменных. Даже стараемся избегать одиночек. Но вопрос в том, что случится с его частью кода, заимствованной из вики, если она будет запущена в одном потоке. Это склонно к недетерминированному поведению.

http://en.wikipedia.org/wiki/Reentrancy_%28computing%29

int g_var = 1;

int f()
{
g_var = g_var + 2;
return g_var;
}

int g()
{
return f() + 2;
}

Другая часть — люди говорят, что повторный вход и безопасность потока не связаны. Однако в этом примере мы можем сделать результаты предсказуемыми, поместив мьютекс в функцию g. Таким образом, когда два потока выполняют этот g () одновременно, результаты являются детерминированными. Итак, мы на самом деле исправили не входящий в систему код, установив потокобезопасный механизм. Пожалуйста, очистите мои концепции здесь. Я что-то упустил или мое понимание неверно?

1

Решение

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

1

Другие решения

Обратите внимание, что эта программа будет скомпилирована примерно так:

int g_var = 1;

int f()
{
int tmp = gvar;
// hardware interrupt might invoke isr() here!
tmp += 2;
g_var = tmp;
return tmp;
}

int g()
{
int tmp = f();
return tmp + 2;
}

Так что если f() прервана в середине и вновь, два вызова f() вырастет g_var только на 2, а не на 4.

0

Вы должны позаботиться в следующих ситуациях:

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

Код, который вы разместили, не использует ничего из вышеперечисленного, поэтому он будет отлично работать.

Другая часть — люди говорят, что повторный вход и безопасность потока не связаны.

Повторное участие обычно означает «этот код написан таким образом, что ему не нужны защитные механизмы». Например, функция, которая использует только локальные переменные и не вызывает библиотечные вызовы, является входной.

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

Противоположность реентранта — не реентрант. И когда вы исправляете не повторяющийся код, вы делаете его поточно-ориентированным. Таким образом, термины связаны, но не являются синонимами: они означают разные вещи.

0

Проблемы повторного входа не являются уникальными для многопоточного кода.

Рассмотрим следующий (однопотоковый) код:

time_t t = time(NULL);
struct tm *a;
struct tm *b;

a = localtime(&t);
t += 3600;
b = localtime(&t);

Здесь у вас есть проблема, так как localtime () не реентерабелен.
Он (обычно) возвращает указатель на статическое хранилище.
Таким образом, в то время как struct tm a а также b указатели должны иметь разное содержание, они теперь одинаковы. a == b, так как последний вызов изменит ту же структуру, что a указывает на.

Приведенный выше код может быть исправлен для однопоточной программы, например так:

time_t t = time(NULL);
struct tm *tmp;
struct tm a;
struct tm b;

tmp = localtime(&t);
a = *tmp;
t += 3600;
b = localtime(&t);
b = *tmp;

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

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

static mutex lmutex;
struct my_localtime(time_t *t, struct tm *result)
{
struct tm *tmp;

mutex_lock(&lmutex);
tmp = localtime(tmp);
*result = *tmp;
mutex_unlock(&lmutex);
}
0
По вопросам рекламы [email protected]