В этой статье говорится:
«функция может быть либо реентерабельной, поточно-ориентированной, и той, и другой, или ни одной из них».
Там также говорится:
«Нереентерабельные функции небезопасны для потоков».
Я вижу, как это может вызвать путаницу. Они означают, что стандартные функции, задокументированные как не требующие повторного входа, также не обязаны быть потокобезопасными, что справедливо для библиотек POSIX iirc (и POSIX объявляет это истинным также и для библиотек ANSI/ISO, ISO имеет нет концепции потоков и, следовательно, нет концепции потокобезопасности). Другими словами, «если функция говорит, что она не реентерабельна, то она также говорит, что она небезопасна для потоков». Это не логическая необходимость, это просто условность.
Вот некоторый псевдокод, который является потокобезопасным (ну, есть много возможностей для обратных вызовов создавать тупиковые ситуации из-за инверсии блокировки, но давайте предположим, что документация содержит достаточно информации для пользователей, чтобы избежать этого), но не является повторно вводимым. Предполагается увеличить глобальный счетчик и выполнить обратный вызов:
take_global_lock();
int i = get_global_counter();
do_callback(i);
set_global_counter(i+1);
release_global_lock();
Если обратный вызов снова вызовет эту процедуру, что приведет к другому обратному вызову, то оба уровня обратного вызова получат один и тот же параметр (что может быть в порядке, в зависимости от API), но счетчик будет увеличен только один раз (что почти наверняка не является API вы хотите, поэтому его придется запретить).
Конечно, если блокировка рекурсивная. Если блокировка нерекурсивная, то, конечно, код в любом случае нереентерабельный, так как второй раз взять блокировку не получится.
Вот некоторый псевдокод, который является "слабо реентерабельным", но не потокобезопасным:
int i = get_global_counter();
do_callback(i);
set_global_counter(get_global_counter()+1);
Теперь нормально вызывать функцию из обратного вызова, но небезопасно вызывать функцию одновременно из разных потоков. Также небезопасно вызывать его из обработчика сигнала, потому что повторный вход из обработчика сигнала может также нарушить счет, если сигнал произойдет в нужное время. Таким образом, код является нереентерабельным по правильному определению.
Вот некоторый код, который, возможно, полностью реентерабельный (за исключением того, что я думаю, что стандарт различает реентерабельный и «непрерываемый сигналами», и я не уверен, где это падает), но все еще не является потокобезопасным:
int i = get_global_counter();
do_callback(i);
disable_signals(); // and any other kind of interrupts on your system
set_global_counter(get_global_counter()+1);
restore_signal_state();
В однопоточном приложении это нормально, если предположить, что ОС поддерживает отключение всего, что необходимо отключить. Это предотвращает повторный вход в критической точке. В зависимости от того, как отключены сигналы, вызов из обработчика сигналов может быть безопасным, хотя в этом конкретном примере все еще остается проблема параметра, передаваемого обратному вызову, который является одним и тем же для отдельных вызовов. Тем не менее, это все еще может пойти не так, как надо, в многопоточном режиме.
На практике непотокобезопасность часто подразумевает неповторяемость, поскольку (неформально) все, что может пойти не так из-за того, что поток прерывается планировщиком, и функция, вызванная повторно из другого потока, также может пойти не так, если поток прерывается сигналом, и функция снова вызывается из обработчика сигнала. Но тогда «исправление» для предотвращения сигналов (отключение их) отличается от «исправления» для предотвращения параллелизма (обычно блокировки). Это в лучшем случае эмпирическое правило.
Обратите внимание, что я подразумевал здесь глобальные переменные, но точно такие же соображения были бы применимы, если бы функция принимала в качестве параметра указатель на счетчик и блокировку. Просто различные случаи будут небезопасными для потоков или не будут повторно входить при вызове с одним и тем же параметром, а не при вызове вообще.
person
Steve Jessop
schedule
09.12.2008