Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Основные API
Разработчики иногда сталкиваются с рядом распространенных проблем при использовании методов Write классов FileIO и PathIO для выполнения операций ввода-вывода файловой системы. Например, распространенные проблемы включают:
- Файл частично записан.
- Приложение получает исключение при вызове одного из методов.
- Операции оставляют после себя файлы .TMP с именем, похожим на имя целевого файла.
Методы записи классов FileIO и PathIO включают следующие:
- WriteBufferAsync
- WriteBytesAsync
- WriteLinesAsync
- WriteTextAsync
В этой статье содержатся сведения о том, как работают эти методы, чтобы разработчики лучше понимали, когда и как их использовать. Эта статья содержит рекомендации и не пытается предоставить решение для всех возможных проблем ввода-вывода файлов.
Замечание
В этой статье рассматриваются методы FileIO в примерах и обсуждениях. Однако методы PathIO соответствуют аналогичному шаблону, и большинство рекомендаций в этой статье также применяются к этим методам.
Удобство и управление
Объект StorageFile не является дескриптором файла, как родная модель программирования Win32. Вместо этого файл StorageFile представляет собой представление файла с методами для управления его содержимым.
Понимание этой концепции полезно при выполнении операций ввода-вывода с помощью StorageFile. Например, раздел "Запись в файл" представляет три способа записи в файл:
- Использование метода FileIO.WriteTextAsync .
- Создав буфер и вызвав метод FileIO.WriteBufferAsync .
- Четырехэтапная модель с помощью потока:
- Откройте файл, чтобы получить поток.
- Получите выходной поток.
- Создайте объект DataWriter и вызовите соответствующий метод Write.
- Зафиксируйте данные в записи данных и очистите выходной поток.
Первые два сценария являются наиболее часто используемыми приложениями. Запись в файл одним действием упрощает кодирование и обслуживание, а также освобождает приложение от необходимости справляться со многими сложностями операций ввода-вывода файлов. Однако это удобство сопряжено с потерей: утрата контроля над всей операцией и возможность обнаруживать ошибки в конкретных точках.
Модель транзакций
Методы записи классов FileIO и PathIO упаковывают шаги в третью модель записи, описанную выше, с добавленным слоем. Этот слой заключён в транзакцию хранения.
Чтобы защитить целостность исходного файла в случае ошибки при записи данных, методы записи используют транзакционные модели, открыв файл с помощью OpenTransactedWriteAsync. Этот процесс создает объект StorageStreamTransaction . После создания этого объекта транзакции API записывают данные, описанные в примере доступа к файлам , или в примере кода в статье StorageStreamTransaction .
На следующей схеме показаны базовые задачи, выполняемые методом WriteTextAsync в успешной операции записи. На этом рисунке представлено упрощенное представление операции. Например, он пропускает такие шаги, как кодировка текста и асинхронное завершение в разных потоках.
Преимущества использования методов записи классов FileIO и PathIO вместо более сложной четырехэтапной модели с помощью потока:
- Один вызов API для обработки всех промежуточных шагов, включая ошибки.
- Исходный файл хранится, если что-то пошло не так.
- Система будет поддерживаться в максимально чистом состоянии.
Однако с таким количеством возможных промежуточных точек сбоя возникает повышенная вероятность сбоя. При возникновении ошибки может быть трудно понять, где произошел сбой процесса. В следующих разделах рассматриваются некоторые ошибки, которые могут возникнуть при использовании методов записи, и предлагаются возможные решения.
Распространенные коды ошибок для методов записи классов FileIO и PathIO
В этой таблице представлены распространенные коды ошибок, с которыми сталкиваются разработчики приложений при использовании методов записи. Шаги в таблице соответствуют шагам на предыдущей схеме.
| Имя ошибки (значение) | Шаги | Причины | Решения |
|---|---|---|---|
| ERROR_ACCESS_DENIED (0X80070005) | 5 | Исходный файл может быть помечен для удаления, возможно, из предыдущей операции. | Повторите операцию. Убедитесь, что доступ к файлу синхронизирован. |
| Ошибка_Нарушения_Общего_Доступа (0x80070020) | 5 | Исходный файл открывается другой эксклюзивной записью. | Повторите операцию. Убедитесь, что доступ к файлу синхронизирован. |
| НЕВОЗМОЖНО УДАЛИТЬ ЗАМЕНЁННОЕ (0x80070497) | 19-20 | Исходный файл (file.txt) не удалось заменить, так как он используется. Еще один процесс или операция получили доступ к файлу до его замены. | Повторите операцию. Убедитесь, что доступ к файлу синхронизирован. |
| ERROR_DISK_FULL (0x80070070) | 7, 14, 16, 20 | Транзакция модели создает дополнительный файл, и это потребляет дополнительное хранилище. | |
| Ошибка: недостаточно памяти (ERROR_OUTOFMEMORY, 0x8007000E) | 14, 16 | Это может произойти из-за нескольких невыполненных операций ввода-вывода или больших размеров файлов. | Более детализированный подход, связанный с управлением потоком, может устранить ошибку. |
| E_FAIL (0x80004005) | Любое | Прочее | Повторите операцию. Если это по-прежнему не удается, это ошибка платформы, и приложение должно завершить работу, так как оно находится в несогласованном состоянии. |
Другие рекомендации по состоянию файла, которые могут привести к ошибкам
Помимо ошибок, возвращаемых методами записи, ниже приведены некоторые рекомендации по тому, что приложение может ожидать при записи в файл.
Данные записываются в файл только в том случае, если операция завершена
Ваше приложение не должно делать никаких предположений о данных в файле во время выполнения операции записи. Попытка получить доступ к файлу до завершения операции может привести к несогласованным данным. Ваше приложение должно отвечать за отслеживание невыполненных операций ввода-вывода.
Читатели
Если файл, в который ведется запись, также используется вежливым читателем (то есть открыт с помощью FileAccessMode.Read), последующие операции чтения приведут к ошибке ERROR_OPLOCK_HANDLE_CLOSED (0x80070323). Иногда приложения повторно открывают файл для чтения, пока операция записи продолжается. Это может привести к состоянию гонки, в котором запись в конечном счете завершается ошибкой при попытке перезаписать исходный файл, так как его невозможно заменить.
Файлы из KnownFolders
Ваше приложение может быть не единственным приложением, которое пытается получить доступ к файлу, который находится в любой из известных папок. Нет гарантии, что если операция выполнена успешно, контент, записанный приложением в файл, останется неизменным при следующей попытке прочитать файл. Кроме того, ошибки, связанные с общим или запрещённым доступом, становятся более распространёнными в этом сценарии.
Конфликт ввода-вывода
Вероятность ошибок одновременного доступа может быть снижена, если наше приложение использует методы записи для файлов в локальном хранилище данных, но всё равно требуется определённая осторожность. Если несколько операций записи отправляются одновременно в файл, нет никаких гарантий относительно того, какие из данных будут записаны в файл. Чтобы устранить эту проблему, рекомендуется сериализовать операции записи в файл.
~TMP-файлы
Иногда, если операция принудительно отменена (например, если приложение было приостановлено или завершено ОС), транзакция не фиксируется или закрывается соответствующим образом. Это может оставить файлы с расширением (~TMP). При обработке активации приложения рекомендуется удалить эти временные файлы (если они существуют в локальных данных приложения).
Рекомендации, основанные на типах файлов
Некоторые ошибки могут стать более распространенными в зависимости от типа файлов, частоты доступа к ним и их размера. Как правило, есть три категории файлов, к которых может получить доступ ваше приложение:
- Файлы, созданные и редактируемые пользователем в локальной папке данных приложения. Они создаются и редактируются только при использовании приложения, и они существуют только в приложении.
- Метаданные приложения. Приложение использует эти файлы для отслеживания собственного состояния.
- Другие файлы в местах файловой системы, где приложение объявило права доступа. Чаще всего они находятся в одном из известных папок.
Приложение имеет полный контроль над первыми двумя категориями файлов, так как они являются частью файлов пакета приложения и получают доступ исключительно к приложению. Для файлов в последней категории приложение должно учитывать, что другие приложения и службы ОС могут одновременно получать доступ к файлам.
В зависимости от приложения доступ к файлам может отличаться от частоты:
- Очень низкий. Обычно это файлы, которые открываются один раз при запуске приложения и сохраняются при приостановке приложения.
- Низко. Это файлы, с которыми пользователь специально выполняет действие (например, сохранение или загрузка).
- Средний или высокий. Это файлы, в которых приложение должно постоянно обновлять данные (например, функции автосохранений или отслеживание метаданных констант).
Для размера файла рассмотрим данные о производительности в следующей диаграмме для метода WriteBytesAsync. На этой диаграмме сравнивается время завершения операции в зависимости от размера файла, по средней производительности 10000 операций для каждого размера файла в контролируемой среде.
Значения времени на оси y опущены намеренно из этой диаграммы, так как разные аппаратные и конфигурации будут выдавать разные абсолютные значения времени. Однако мы последовательно наблюдали эти тенденции в наших тестах:
- Для очень небольших файлов (<= 1 МБ): операции выполняются consistently быстро.
- Для больших файлов (> 1 МБ): время завершения операций начинает увеличиваться экспоненциально.
Ввод-вывод во время приостановки приложения
Приложение должно обрабатывать приостановку, если вы хотите сохранить сведения о состоянии или метаданные для использования в последующих сеансах. Дополнительные сведения о приостановке приложений см. в статье о жизненном цикле приложений и этой записи блога.
Если ОС не предоставляет расширенное выполнение приложению, когда приложение приостановлено, оно имеет 5 секунд, чтобы освободить все ресурсы и сохранить свои данные. Для обеспечения оптимальной надежности и взаимодействия с пользователем всегда предполагается, что время, необходимое для обработки задач приостановки, ограничено. Помните следующие рекомендации в течение 5-секундного периода для обработки задач приостановки выполнения:
- Старайтесь обеспечить минимальное количество операций ввода-вывода, чтобы избежать условий гонки, вызванных операциями очистки и выпуска.
- Избегайте написания файлов, которые требуют сотни миллисекунда или более для записи.
- Если приложение использует методы записи, имейте в виду все промежуточные шаги, необходимые этим методам.
Если приложение работает с небольшим количеством данных состояния во время приостановки, в большинстве случаев можно использовать методы записи для очистки данных. Однако если приложение использует большое количество данных о состоянии, рассмотрите возможность использования потоков для непосредственного хранения данных. Это может помочь уменьшить задержку, представленную транзакционной моделью методов записи.
Пример см. в примере BasicSuspension .
Другие примеры и ресурсы
Ниже приведены несколько примеров и других ресурсов для конкретных сценариев.
Пример кода для повторных попыток ввода-вывода файла
Ниже приведен пример псевдокода о том, как повторить запись (C#), предполагая, что запись выполняется после того, как пользователь выбирает файл для сохранения:
Windows.Storage.Pickers.FileSavePicker savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.FileTypeChoices.Add("Plain Text", new List<string>() { ".txt" });
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
Int32 retryAttempts = 5;
const Int32 ERROR_ACCESS_DENIED = unchecked((Int32)0x80070005);
const Int32 ERROR_SHARING_VIOLATION = unchecked((Int32)0x80070020);
if (file != null)
{
// Application now has read/write access to the picked file.
while (retryAttempts > 0)
{
try
{
retryAttempts--;
await Windows.Storage.FileIO.WriteTextAsync(file, "Text to write to file");
break;
}
catch (Exception ex) when ((ex.HResult == ERROR_ACCESS_DENIED) ||
(ex.HResult == ERROR_SHARING_VIOLATION))
{
// This might be recovered by retrying, otherwise let the exception be raised.
// The app can decide to wait before retrying.
}
}
}
else
{
// The operation was cancelled in the picker dialog.
}
Синхронизация доступа к файлу
Параллельный программирование с помощью блога .NET — отличный ресурс для руководства по параллельному программированию. В частности, в записи об AsyncReaderWriterLock описывается, как поддерживать монопольный доступ к файлу для записи при одновременном доступе на чтение. Помните, что сериализация операций ввода-вывода влияет на производительность.
См. также
Windows developer