Повторная входимость кода по сравнению с потокобезопасностью

26
задан Codex 30 April 2012 в 08:54
поделиться

2 ответа

Повторно используемый код не имеет никакого состояния в единственной точке. Можно назвать код, в то время как что-то выполняется в коде. Если код использует глобальное состояние, один вызов может очевидно перезаписать глобальное состояние, повредив вычисление в другом вызове.

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

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

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

21
ответ дан Maarten 28 November 2019 в 07:52
поделиться

В той статье говорится:

"функция может быть или повторно используема, ориентирована на многопотоковое исполнение, оба или ни один".

Это также говорит:

"Неповторно используемые функции небезопасны потоком".

я вижу, как это может вызвать путаницу. Они означают, что стандартные функции, зарегистрированные как не требуемый быть повторно используемыми, также не требуются быть ориентированными на многопотоковое исполнение, который верен для библиотек 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();

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

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

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

8
ответ дан Steve Jessop 28 November 2019 в 07:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: