У нас есть класс C ++, который в основном читает и записывает векторы из двоичного файла. Примерная функция чтения, загружающая в память один вектор, выглядит так:
int load (const __int64 index, T* values) const {
int re = _fseeki64(_file, index * _vectorSize + _offsetData, SEEK_SET);
assert(re == 0);
size_t read = fread(values, sizeof(T), _vectorElements, _file);
assert(read == _vectorElements);
return 0;}
Наши программы являются многопоточными с OpenMP, и несколько потоков одновременно обращаются к одному и тому же файлу. Чтобы избежать проблем из-за нескольких потоков, мы всегда закрываем вызов функции в рамках критического оператора OpenMP:
#pragma omp critical {
load(...);
}
Я знаю, что среда выполнения Microsoft Visual C ++ содержит несколько функций, таких как _fseek_nolock
, _fread_nolock
, _fwrite_nolock
и так далее ... Например, функция _fread_nolock()
описывается как
Эта функция представляет собой неблокирующую версию fread. Он идентичен fread, за исключением того, что он не защищен от вмешательства других потоков. Это может быть быстрее, потому что это не требует накладных расходов на блокировку других потоков. Используйте эту функцию только в потокобезопасных контекстах, таких как однопоточные приложения или где вызывающая область уже обрабатывает изоляцию потока.
Теперь мой вопрос: я понимаю, что функция блокирует «повторные» вызовы, поэтому никакой другой поток не войдет в функцию до того, как другие потоки вернутся. Однако я не понимаю, почему необходимо таким образом защищать одну функцию. IMHO все функции, которые обращаются / изменяют указатель файла (_file
в примере кода), должны быть защищены и, следовательно, быть поточно-ориентированными. Это требует создания блокировки вокруг всего функционального блока, который на самом деле вызывает стандартные функции C fseek и fread, поэтому я не вижу смысла предоставлять такие неблокирующие функции.
Может ли кто-нибудь объяснить мне эти механизмы блокировки, потому что я полагаю, что наша параноидальная схема блокировки тратит впустую некоторую производительность?
Заранее спасибо!
3 ответа
Для некоторого простого кода достаточно блокировки в ФАЙЛЕ *. Рассмотрим базовую инфраструктуру ведения журнала, в которой вы хотите, чтобы все потоки регистрировались через общий ФАЙЛ *. Внутренняя блокировка гарантирует, что ФАЙЛ * не будет поврежден несколькими потоками, и, поскольку каждая строка журнала должна быть автономной, не имеет значения, как отдельные вызовы чередуются.
_fseek_nolock
. Эта функция всегда требует, чтобы вторая функция сообщала указателю файла.
Если вы используете многопоточную среду выполнения C Microsoft, все функции, которым требуются глобальные или статические переменные, будут просто работать должным образом (например, printf и fread, не спрашивайте меня, зачем им глобальные переменные). Однако вы по-прежнему не можете передать структуру FILE * функции, которая записывает в нее, и ожидать, что она будет потокобезопасной.
Таким образом, «потокобезопасные» функции Microsoft являются потокобезопасными только в том смысле, что они реентерабельны, то есть весь доступ к глобальным объектам и статике осуществляется с помощью мьютекса или подобного. Но не в том смысле, что вы можете одновременно вызывать две fprintf () с одним и тем же ФАЙЛОМ *.
Источник: http://msdn.microsoft.com/ en-us / library / 1bh5ewb2% 28VS.71% 29.aspx
Если ваше приложение уже гарантирует сериализованный доступ к дескрипторам файлов, вы можете повысить производительность, если укажете среде выполнения c обойти ее собственную сериализацию. Это назначение функций _fread_nolock и т. Д.
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .