Ответ 1
Чтобы ответить на все 5 вопросов:
1) Забор компилятора (сам по себе без забора процессора) полезен только в двух ситуациях:
-
Чтобы обеспечить ограничение порядка хранения между одним потоком и обработчиком асинхронного прерывания, привязанным к тому же потоку (например, обработчик сигнала).
-
Чтобы обеспечить ограничение порядка хранения между несколькими потоками, когда гарантируется, что каждый поток будет выполняться на одном ядре ЦП. Другими словами, приложение будет работать только в одном ядре, или приложение принимает специальные меры (через сродство к процессору), чтобы гарантировать, что каждый поток, который разделяет данные, привязан к одному и тому же ядру.
2) Модель памяти базовой архитектуры, будь то сильно или слабо упорядоченная, не имеет никакого отношения к тому, нужна ли компиляция в ситуации.
3) Вот псевдокод, который демонстрирует использование ограждения компилятора сам по себе для достаточной синхронизации доступа к памяти между потоком и обработчиком сигнала async, связанным с одним и тем же потоком:
void async_signal_handler()
{
if ( is_shared_data_initialized )
{
compiler_only_memory_barrier(memory_order::acquire);
... use shared_data ...
}
}
void main()
{
// initialize shared_data ...
shared_data->foo = ...
shared_data->bar = ...
shared_data->baz = ...
// shared_data is now fully initialized and ready to use
compiler_only_memory_barrier(memory_order::release);
is_shared_data_initialized = true;
}
Важное примечание: В этом примере предполагается, что async_signal_handler
привязан к тому же потоку, который инициализирует shared_data
и устанавливает флаг is_initialized
, что означает, что приложение однопоточно или оно соответственно устанавливает маски сигналов нитей. В противном случае забор компилятора будет недостаточным, и понадобится забор ЦП.
4) Они должны быть одинаковыми. acq_rel
и seq_cst
должны приводить к полному (двунаправленному) заграждению компилятора, без каких-либо связанных с ограждением процессоров. Понятие "последовательная согласованность" вступает в игру только при задействовании нескольких ядер и потоков, а atomic_signal_fence
относится только к одному потоку выполнения.
5) Нет. (Если, конечно, доступ к потоковым локальным данным не выполняется из асинхронного обработчика сигнала, и в этом случае может потребоваться ограждение компилятора.) В противном случае забор никогда не понадобится с поточно-локальные данные, поскольку компилятору (и ЦП) разрешено только изменять порядок доступа к памяти таким образом, чтобы не изменять наблюдаемое поведение программы в отношении ее точек с однопоточной перспективы. И логически можно думать, что потоковая локальная статика в многопоточной программе должна быть такой же, как глобальная статика в однопоточной программе. В обоих случаях данные доступны только из одного потока, что предотвращает возникновение гонки данных.