Должен ли я повторно использовать объект FileStream / BinaryWriter? - PullRequest
2 голосов
/ 15 октября 2010

Обновление: После просмотра журнала событий примерно в то время, когда это произошло, я получаю сообщение: «Серверу не удалось выделить из системного невыгружаемого пула, поскольку пул был пуст. " повторяется непрерывно по всему журналу, пока он не будет перезагружен.

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

Проблема, с которой я сталкиваюсь, заключается в том, что после длительного периода времени (4 часа и более) мое приложение вылетает и, похоже, удаляет Windows; Я больше не могу открыть Windows Explorer или любое другое приложение. Перезагрузка системы, кажется, решает проблему, однако, когда я делаю файл, в который я пишу, пуст.

Это заставляет меня думать, что, возможно, проблема связана с дескрипторами открытых файлов; возможно, Windows как-то достигает своего предела количества открытых файловых дескрипторов?

Итак, здесь возникает связанный с этим вопрос; Вот основная функция, которая записывает данные в файл. Как видите, объекты FileStream и BinaryWriter создаются при каждом вызове этой функции, заключаются в операторы, чтобы обеспечить их правильное закрытие / удаление.

/// <summary>
/// This is called after changing any
/// stats data, or on initial startup.
/// It saves the current stats to file.
/// </summary>
public void UpdateStatsData()
{
    lock (this.lockObject)
    {
        using (FileStream fileStream = new FileStream(Constants.StatsFile, FileMode.Create, FileAccess.Write, FileShare.None, 128, FileOptions.WriteThrough))
        {
            using (BinaryWriter binWriter = new BinaryWriter(fileStream))
            {
                binWriter.Write(this.serverStats.APM);
                binWriter.Write(this.serverStats.AverageJackpotWin);
                binWriter.Write(this.serverStats.AverageWinnings);
                binWriter.Write(this.serverStats.NumberOfGamesPlayed);
                binWriter.Write(this.serverStats.NumberOfJackpots);
                binWriter.Write(this.serverStats.RunningPercentage);
                binWriter.Write(this.serverStats.SiteID);
                binWriter.Write(this.serverStats.TotalJackpotsValue);
                binWriter.Write(this.serverStats.TotalStaked);
                binWriter.Write(this.serverStats.TotalWinnings);
            }
        }
    }
}
  1. Возможно ли, что эта функция при очень быстром вызове может вызвать медленное создание дескрипторов файлов и в конечном итоге превысить максимум Windows?

  2. Возможное решение заключается в том, чтобы сделать объекты FileStream и BinaryWriter закрытыми переменными-членами класса, создать их в конструкторе, а затем перезаписать данные при каждом вызове.

.

/// <summary>
/// This should be called after changing any
/// stats data, or on initial startup.
/// It saves the current stats to a serialized file.
/// </summary>
public void UpdateStatsData()
{
    lock (this.lockObject)
    {
        // Seek to the beginning of the file.
        this.binWriter.BaseStream.Seek(0, SeekOrigin.Begin);

        // Write the stats data over the existing data.
        this.binWriter.Write(this.serverStats.APM);
        this.binWriter.Write(this.serverStats.AverageJackpotWin);
        this.binWriter.Write(this.serverStats.AverageWinnings);
        this.binWriter.Write(this.serverStats.NumberOfGamesPlayed);
        this.binWriter.Write(this.serverStats.NumberOfJackpots);
        this.binWriter.Write(this.serverStats.RunningPercentage);
        this.binWriter.Write(this.serverStats.SiteID);
        this.binWriter.Write(this.serverStats.TotalJackpotsValue);
        this.binWriter.Write(this.serverStats.TotalStaked);
        this.binWriter.Write(this.serverStats.TotalWinnings);
    }
}

Однако, хотя это может быть быстрее и означать использование только одного FileStream, как мне обеспечить, чтобы FileStream и BinaryWriter были закрыты / удалены должным образом при завершении работы приложения?

Ответы [ 4 ]

2 голосов
/ 15 октября 2010

Недостаточно памяти невыгружаемого пула в Windows - очень серьезная ошибка. Ничего хорошего после этого не произойдет, драйверы не справятся со своей задачей, для этого требуется перезагрузка.

Конечно, это не нормально для программы пользовательского режима (управляемой), чтобы это происходило. Windows защищает себя от этого, предоставляя процессу ограниченную квоту доступных системных ресурсов. Их много, ограничение в 10 000 дескрипторов - очевидный, который срабатывает довольно часто, если программа пропускает дескрипторы.

Память из невыгружаемого пула выделяется исключительно драйверами. Им нужна такая драгоценная память, потому что они используют память во время прерывания устройства. Критическое время, когда невозможно отобразить память из файла подкачки. Пул маленький, так должно быть, потому что он постоянно занимает оперативную память. Это зависит от объема ОЗУ вашей машины, обычно не более 256 МБ для компьютера с 1 ГБ ОЗУ. Вы можете увидеть его текущий размер в TaskMgr.exe, вкладка «Производительность». Я даю ему достойный обходной путь, сейчас он показывает 61 МБ.

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

2 голосов
/ 15 октября 2010

Комбинация параметров в конструкторе FileStream выглядит подозрительно для меня (при условии, что все потоки записывают в один и тот же файл (Constants.StatsFile):

  • FileMode.Create = Всегда создавать файл. перезаписать, если он существует. Вы удаляете все предыдущие журналы с каждой записью в этом методе (может попробовать OpenOrCreate или Append)
  • FileOptions.WriteThrough = нет кэширования - заставить диск вращаться и заставить поток ждать диска - медленно

Мое предположение: вы вызываете этот метод гораздо быстрее, чем он может завершить. Каждый вызов выполняет резервное копирование оператора lock, ожидающего предыдущего вызова, чтобы удалить файл, записать в него и полностью сбросить его на диск. Через некоторое время у вас просто не хватит памяти.

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

using (FileStream fileStream = new FileStream(Constants.StatsFile, FileMode.Append, 
        FileAccess.Write, FileShare.None, 128, FileOptions.SequentialScan))
1 голос
/ 15 октября 2010

Я не вижу ничего плохого в первом с точки зрения закрытия ручки.Я делаю со вторым;в частности, те самые вопросы, о которых вы спрашиваете.Вы можете сделать свой класс одноразовым, а затем идеально закрыть его во время «контролируемого» выключения, в то время как в зависимости от финализатора файлового объекта позаботиться о проблемах во время исключительного выключения, но я не уверен, что вы исправляетепроблема.

Какие измерения дескрипторов открытых файлов подтверждают ваше подозрение, что это проблема?Разумно подозревать дескрипторы открытых файлов, когда вы действительно открываете много файлов, но глупо «исправлять» это, если ни A) проверка кода не покажет, что у него, очевидно, возникнет эта проблема (не здесь), либо B) выпоказано, что такие дескрипторы файлов действительно слишком высоки.

Оставляет ли приложение исключение в программе просмотра событий при сбое?

1 голос
/ 15 октября 2010

Большая часть этого кода выглядит хорошо для меня - у вас не должно быть проблем с повторным созданием FileStreams, как вы.

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

...