#c# #file-io #locking
#c# #file-io #блокировка
Вопрос:
Мне нужно иметь возможность открывать файл для его чтения, сохраняя блокировку, которая запрещает другим экземплярам того же приложения доступ на запись, пока я не запишу исправленный файл обратно на диск. Файл находится в общем расположении в сети, и экземпляры приложения могут находиться на любом компьютере в сети.
Я пытался использовать a FileStream
с FileAccess.ReadWrite
и FileShare.Read
, с a Streamreader
для чтения, а затем a StreamWriter
(на том же FileStream
) для записи, но это повреждает файл. Другие перестановки FileAccess
и FileShare
, похоже, тоже не решают мою основную проблему.
Итак, я попытался закрыть StreamReader
перед открытием StreamWriter
, но это изменяет свойства CanRead
and CanWrite
FileStream
, поэтому я все еще не могу писать.
Очевидно, что я использую неправильный подход, так что может кто-нибудь сказать мне, как я должен подходить к этому? Кажется, это достаточно распространенная вещь, которую нужно сделать — отредактировать файл и заблокировать доступ на запись, пока отредактированный файл не будет сохранен.
Ответ №1:
Если вы хотите выполнить запись в файл, вам необходимо получить эксклюзивный доступ к файлу, иначе другие программы могут прочитать частично записанные данные (ваши записи не являются атомарными). Для этого есть решения, но они довольно сложные.
Решение может быть примерно таким:
static bool Read(FileStream fs, byte[] data, long position)
{
fs.Seek(position, SeekOrigin.Begin);
if (fs.ReadByte() == 0)
{
// Block of data not finished writing
return false;
}
fs.Read(data, 0, data.Length);
return true;
}
static bool Write(FileStream fs, byte[] data, long position)
{
try
{
fs.Lock(position, data.Length 1);
fs.Seek(position, SeekOrigin.Begin);
fs.WriteByte(0);
fs.Write(data, 0, data.Length);
fs.Seek(position, SeekOrigin.Begin);
fs.WriteByte(1);
fs.Unlock(position, data.Length 1);
return true;
}
catch (IOException)
{
return false;
}
}
static bool Append(FileStream fs, byte[] data)
{
return Write(fs, data, fs.Length);
}
где вы всегда держите открытым FileStream
as
FileStream fs1 = new FileStream("Test.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
Перед data
этим есть «защитный байт», который сообщает, записываются ли данные. Если он записывается, то чтение завершится неудачей. Файл блокируется «там, где это необходимо» с помощью FileStream.Lock
.
Это явно лучше работает с двоичными данными фиксированной длины. Если у вас есть данные переменной длины (или вам нужно атомарно обновлять больше «областей» файла), тогда это становится более сложным. Обычно вы используете DBS по этой причине 🙂
Комментарии:
1. Спасибо. В конце концов я принял подход «заблокировать файл». Я знаю, что это все еще не атомарно, но я полагаю, что риск столкновения невелик.
2. Спасибо. В конце концов я принял подход «заблокировать файл». Я знаю, что это все еще не атомарно, но я полагаю, что риск столкновения невелик. например, проверьте, существует ли файл блокировки. Если это произойдет, заблокируйте операцию редактирования. Если этого не произойдет, создайте файл блокировки, выполните редактирование, сохраните файл, а затем удалите файл блокировки. Существует потенциальное окно между тестированием файла блокировки и его созданием, в котором другой пользователь потенциально может сохранить изменения первого пользователя, но окно маленькое, и это случайная операция, поэтому я не слишком беспокоюсь об этом.
3. В качестве альтернативы, как мне получить эксклюзивный доступ, чтобы первый пользователь мог читать / записывать?
4. Неясно, используете ли вы «двоичный» файл или «текстовый» файл.
5. @haughtonomous написал пример
Ответ №2:
Держите файл открытым с доступом на запись. Это должно запретить все другие записи в файл.
Комментарии:
1. Да, но мне нужно, чтобы другие могли читать файл в то же время. Это необходимо для запуска приложения.
2. новый поток файлов(имя_файла, режим файла. Откройте или создайте, доступ к файлу. Запись, файловый ресурс.Чтение);
3. Спасибо. К сожалению, это мешает мне прочитать файл. Мне нужно читать, а затем записывать, и все это без разрешения экземпляров доступа на запись в приложение (но они должны иметь доступ только для чтения)
4. Доступ к файлам. Затем перезапись. Читатели могут проверять часовые (например, перевод строки) и использовать Seek() в случае неполных данных.
Ответ №3:
Поскольку вы не указываете, какую ОС вы используете, и не указываете, является ли доступ критичным по времени или порядку, я думаю, что лучше всего использовать каталог, чтобы указать, что файл заблокирован.
В большинстве ОС создание каталога происходит практически мгновенно, в то время как создание файла — нет. Поэтому каждый раз, когда ваше приложение хочет получить доступ к рассматриваемому файлу данных, оно должно попытаться создать подкаталог в том же месте, что и файл. Если каталог уже существует, то создание завершится неудачно, и ваше приложение должно подождать, повторяя попытку, пока она не завершится успешно.
Когда ему удается создать каталог, он может получить доступ к файлу и записать его. После того, как все данные были записаны и доступ к файлу закрыт, следует немного подождать (чтобы завершить фактическую запись), прежде чем удалять каталог.
Однако вам нужно будет написать некоторый код, который может обработать случай сбоя вашего приложения и оставить существующий каталог блокировки.
Комментарии:
1. Это Windows XP и 7. Применяются ли те же комментарии к скорости создания? Я думал, что каталог — это просто особый вид файла (или это Unix?)
2. Интересно, что я только что написал небольшой тест, создав 100 файлов (File. Создать (…)) и создать 100 каталогов (Directory. CreateDirectory(…)), и файлы заняли 1/4 времени, затраченного на создание каталогов. Файлы = в 4 раза быстрее каталогов. Это, похоже, противоречит вашему утверждению об относительной скорости.