Блокировка файлов — чтение, затем запись при блокировке

#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 раза быстрее каталогов. Это, похоже, противоречит вашему утверждению об относительной скорости.