Совместное использование файлов не работает должным образом - программирование
Подтвердить что ты не робот

Совместное использование файлов не работает должным образом

У меня проблема с совместным доступом к файлам, когда мой процесс пытается прочитать файл журнала, пока он еще открыт NLog. При диагностике проблемы я нашел что-то удивительное. Не удалось выполнить следующее:

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.Read))
{
}

Второй вызов конструктора FileStream завершается с:

System.IO.IOException was unhandled
  Message=The process cannot access the file 'c:\...\test.file' because it is being used by another process.
  Source=mscorlib
  StackTrace:
       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
       at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)
       at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)

Это несмотря на то, что первый FileStream указывает на его готовность делиться чтением. Я обнаружил еще более удивительным, что это работает:

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}

Ум, да, запрашивая больше доступа при открытии второго потока, фактически обходит проблему. Я совершенно не понимаю, почему это так, и могу только предположить, что я что-то недопонимаю. Я прочитал документы API, но они просто поддерживают мою текущую ментальную модель, как это должно работать, вопреки тому, как она работает.

Ниже приведены некоторые поддерживающие цитаты из docs:

Типичное использование этого перечисления состоит в том, чтобы определить, являются ли два процесса может одновременно читать из того же файла. Например, если файл открыто и Чтение указано, другие пользователи могут открыть файл для но не для записи.

Вот еще один камень:

Следующий конструктор FileStream открывает существующий файл и предоставляет доступ только для чтения для других пользователей (Read).

FileStream s2 = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read);

Может ли кто-нибудь пролить свет на это поведение. Я тестирую это на .NET 4% Windows XP.

4b9b3361

Ответ 1

 var fileStream2 = new FileStream(..., FileShare.Read)

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

Вы должны включить FileShare.Write.

Ответ 2

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

fileStream2 успешно откроет файл, оставляя FileShare.Read как "наследие" для последующего доступа, только если нет процессов, у которых уже есть доступ к файлу Write. Более того, в нашем примере мы говорим об одном и том же процессе. Не было бы слишком большого смысла модифицировать свойства потока файлов из другого потока файлов, не так ли?

Возможно, следующее сравнение объясняет это еще лучше:

// works
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite))
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}

// fails
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}

По-моему, фраза описания для FileShare.Read:

Позволяет продолжить открытие файла для чтения.

следует читать как

Последующий доступ к файлу ограничен только для чтения, включая доступ к уже существующим замкам.

[Обновление]

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

Внутренний FileStream ctor

Внутренний метод FileStream Init

Ответ 3

Я думаю, что нашел ответ в документации для CreateFile.

При обсуждении параметра dwShareMode он говорит:

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

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

Это принципиально меняет мое понимание того, как работает совместное использование файлов.

Ответ 4

Четвертый параметр, который вы передаете

доля
Константа, определяющая способ совместного использования файлов процессами.

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