Совместное использование файлов не работает должным образом
У меня проблема с совместным доступом к файлам, когда мой процесс пытается прочитать файл журнала, пока он еще открыт 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.
Ответы
Ответ 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
Четвертый параметр, который вы передаете
доля
Константа, определяющая способ совместного использования файлов процессами.
определяет, в каком режиме другие могут открыть файл. Таким образом, очевидно, что когда вы пытаетесь открыть файл с файловым режимом "читать" и уже открыть тот же файл в режиме записи - операция завершается с ошибкой.