Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Основные API
Разработчики иногда сталкиваются с рядом распространенных проблем при использовании методов записи FileIO и PathIO для операций ввода-вывода в файловой системе. Например, распространенные проблемы включают:
- Файл частично записан.
- Приложение получает исключение при вызове одного из методов.
- Операции оставляют после себя файлы .TMP с именем, похожим на имя целевого файла.
Методы записи классов FileIO и PathIO включают следующее:
- WriteBufferAsync
- WriteBytesAsync
- WriteLinesAsync
- WriteTextAsync
В этой статье содержатся сведения о том, как работают эти методы, чтобы разработчики лучше понимали, когда и как их использовать. Эта статья содержит рекомендации и не пытается предоставить решение для всех возможных проблем ввода-вывода файлов.
Замечание
В этой статье рассматриваются методы FileIO в примерах и обсуждениях. Однако методы PathIO соответствуют аналогичному шаблону, и большинство рекомендаций в этой статье также применяются к этим методам.
Удобство и управление
Объект StorageFile не является файловым дескриптором, как нативная модель программирования Win32. Вместо этого файл StorageFile представляет собой представление файла с методами для управления его содержимым.
Понимание этой концепции полезно при выполнении операций ввода-вывода с помощью StorageFile. Например, в разделе "Запись в файл" описываются три способа записи в файл:
- Использование метода FileIO.WriteTextAsync .
- Создав буфер и вызвав метод FileIO.WriteBufferAsync .
- Четырехэтапная модель с помощью потока:
- Открыть файл, чтобы получить поток.
- Получить выходной поток.
- Создайте объект
и вызовите соответствующий метод записиDataWriter . - Зафиксировать данные в устройстве записи данных и очистить выходной поток.
Первые два сценария являются наиболее часто используемыми приложениями. Запись в файл за одну операцию облегчает написание и сопровождение кода, а также освобождает приложение от необходимости учитывать многие сложности файловых операций ввода-вывода. Однако это удобство обходится ценой: потеря контроля над всей операцией и способность обнаруживать ошибки в определенных точках.
Модель транзакций
Методы записи классов FileIO и PathIO оборачивают шаги третьей модели записи, описанной выше, добавляя дополнительный слой. Этот слой инкапсулируется в транзакцию хранилища.
Чтобы защитить целостность исходного файла в случае ошибки при записи данных, методы записи используют модель транзакции, открывая файл с помощью OpenTransactedWriteAsync. Этот процесс создает объект StorageStreamTransaction . После создания этого объекта транзакции API записывают данные по аналогии с примером доступа к файлам или примером кода в статье StorageStreamTransaction.
На следующей схеме показаны базовые задачи, выполняемые методом WriteTextAsync в успешной операции записи. На этом рисунке представлено упрощенное представление операции. Например, он пропускает такие шаги, как кодировка текста и асинхронное завершение в разных потоках.
Преимущества использования методов записи классов FileIO и PathIO вместо более сложной четырехшаговой модели с использованием потока:
- Один вызов API для обработки всех промежуточных шагов, включая ошибки.
- Исходный файл хранится, если что-то пошло не так.
- Состояние системы будут поддерживать как можно более чистым.
Однако с таким количеством возможных промежуточных точек сбоя возникает повышенная вероятность сбоя. При возникновении ошибки может быть трудно понять, где произошел сбой процесса. В следующих разделах представлены некоторые из сбоев, которые могут возникнуть при использовании методов записи и предоставляют возможные решения.
Распространенные коды ошибок для методов записи классов FileIO и PathIO
В этой таблице представлены распространенные коды ошибок, с которыми сталкиваются разработчики приложений при использовании методов записи. Шаги в таблице соответствуют шагам на предыдущей схеме.
Имя ошибки (значение) | Этапы | Причины | Решения |
---|---|---|---|
ОШИБКА_ДОСТУП_ЗАПРЕЩЕН (0X80070005) | 5 | Исходный файл может быть помечен для удаления, возможно, из предыдущей операции. | Повторите операцию. Убедитесь, что доступ к файлу синхронизирован. |
ERROR_SHARING_VIOLATION (0x80070020) — ошибка совместного доступа. | 5 | Исходный файл открывается другой эксклюзивной записью. | Повторите операцию. Убедитесь, что доступ к файлу синхронизирован. |
Ошибка: невозможно удалить заменённое (0x80070497) | 19-20 | Исходный файл (file.txt) не удалось заменить, так как он используется. Еще один процесс или операция получили доступ к файлу до его замены. | Повторите операцию. Убедитесь, что доступ к файлу синхронизирован. |
Ошибка: НЕДОСТАТОЧНО МЕСТА НА ДИСКЕ (0x80070070) | 7, 14, 16, 20 | Транзакция модели создает дополнительный файл, и это потребляет дополнительное хранилище. | |
Ошибка нехватки памяти (0x8007000E) | 14, 16 | Это может произойти из-за нескольких невыполненных операций ввода-вывода или больших размеров файлов. | Более детализированный подход, заключающийся в контроле потока, может устранить ошибку. |
E_FAIL (0x80004005) | Любое | Разное | Повторите операцию. Если это по-прежнему не удается, возможно, это ошибка платформы, и приложение должно завершиться, так как оно находится в нестабильном состоянии. |
Другие рекомендации по состоянию файла, которые могут привести к ошибкам
Помимо ошибок, возвращаемых методами записи, вот некоторые рекомендации, чего приложение может ожидать при записи в файл.
Данные записываются в файл только в том случае, если операция завершена
Ваше приложение не должно делать никаких предположений о данных в файле во время выполнения операции записи. Попытка получить доступ к файлу до завершения операции может привести к несогласованным данным. Ваше приложение должно отвечать за отслеживание невыполненных операций ввода-вывода.
Читатели
Если файл, в который выполняется запись, одновременно используется другим процессом для чтения (то есть открыт с помощью FileAccessMode.Read), последующие операции чтения завершатся с ошибкой ERROR_OPLOCK_HANDLE_CLOSED (0x80070323). Иногда приложения повторно пытаются открыть файл для чтения, пока выполняется операция записи. Это может привести к состоянию гонки, в котором запись в конечном итоге терпит неудачу при попытке перезаписать исходный файл, так как его невозможно заменить.
Файлы из KnownFolders
Ваше приложение может быть не единственным приложением, которое пытается получить доступ к файлу, который находится в любой из известных папок. Нет гарантии, что если операция выполнена успешно, содержимое, которое приложение записало в файл, останется неизменным при следующей попытке прочитать файл. Кроме того, ошибки с общим доступом или отказом в доступе встречаются чаще.
Конфликт ввода-вывода
Вероятность ошибок параллелизма может быть снижена, если приложение использует методы записи для файлов в локальных данных, но некоторые осторожность по-прежнему требуется. Если несколько операций записи отправляются одновременно в файл, нет никаких гарантий о том, какие данные заканчиваются в файле. Мы рекомендуем вашему приложению сериализовать операции записи в файл, чтобы устранить эту проблему.
~TMP-файлы
Иногда, если операция принудительно отменена (например, если приложение было приостановлено или завершено ОС), транзакция не фиксируется или закрывается соответствующим образом. Это может оставить файлы с расширением (~TMP). При обработке активации приложения рекомендуется удалить эти временные файлы (если они существуют в локальных данных приложения).
Рекомендации, основанные на типах файлов
Некоторые ошибки могут стать более распространенными в зависимости от типа файлов, частоты доступа к ним и их размера. Как правило, есть три категории файлов, к которых может получить доступ ваше приложение:
- Файлы, созданные и редактируемые пользователем в локальной папке данных приложения. Они создаются и редактируются только при использовании приложения, и они существуют только в приложении.
- Метаданные приложения. Приложение использует эти файлы для отслеживания собственного состояния.
- Другие файлы в расположениях файловой системы, где приложение объявило возможность доступа. Чаще всего они находятся в одной из известных папок.
Приложение имеет полный контроль над первыми двумя категориями файлов, так как они являются частью файлов пакета приложения и получают доступ исключительно к приложению. Для файлов в последней категории приложение должно учитывать, что другие приложения и службы ОС могут одновременно получать доступ к файлам.
В зависимости от приложения доступ к файлам может отличаться от частоты:
- Очень низкий. Обычно это файлы, которые открываются один раз при запуске приложения и сохраняются при приостановке приложения.
- Низко. Это файлы, с которыми пользователь специально выполняет действие (например, сохранение или загрузка).
- Средний или высокий. Это файлы, в которых приложение должно постоянно обновлять данные (например, функции автосохранений или отслеживание метаданных констант).
Для размера файла рассмотрим данные о производительности в следующей диаграмме для метода WriteBytesAsync . На этой диаграмме сравнивается время выполнения операции в зависимости от размера файла при среднем значении 10000 операций на каждый размер файла в контролируемой среде.
Значения времени на оси y опущены намеренно из этой диаграммы, так как разные аппаратные и конфигурации будут выдавать разные абсолютные значения времени. Однако мы последовательно наблюдали эти тенденции в наших тестах:
- Для очень небольших файлов (<= 1 МБ): время выполнения операций постоянно быстрое.
- Для больших файлов (> 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 описывается, как поддерживать исключительный доступ к файлу для записи, позволяя одновременное чтение. Помните, что сериализация операций ввода-вывода влияет на производительность.