c# - чтение из двоичного файла журнала, который обновляется каждые 6 секунд с 12 КБ данных

У меня есть двоичный файл журнала с потоковыми данными с датчика (Int16).
Каждые 6 секунд добавляется 6000 выборок типа Int16, пока датчик не будет отключен.

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

РЕДАКТИРОВАТЬ: На данный момент есть несколько замечательных предложений, необходимо добавить, что «серверное» приложение предоставляется сторонним поставщиком и не может быть изменено.


person cabgef    schedule 23.07.2009    source источник


Ответы (5)


Если он всегда добавляет один и тот же объем данных, может иметь смысл открыть его повторно. Возможно, вы захотите узнать длину перед тем, как открыть ее, а затем округлить до целого числа доступных «наборов образцов» на тот случай, если вы поймаете ее, пока она все еще записывает данные. Это может означать, что вы читаете меньше, чем могли прочитать (если запись заканчивается между проверкой длины и началом чтения), но в следующий раз вы наверстаете упущенное.

Вам нужно убедиться, что вы используете соответствующие параметры обмена, чтобы писатель мог писать, пока вы читаете. (Вероятно, автору придется писать и об этом.)

person Jon Skeet    schedule 23.07.2009
comment
Средство записи нельзя изменить, и оно написано в неуправляемом коде (неизвестно, какой язык или какие параметры он использует для записи данных). Поскольку он обновляется на 12 000 байт с интервалом в 6 секунд, всегда ли я в безопасности, когда он отстает на 6 секунд, даже если он записывает, пока я читаю предыдущий блок (FileShare.ReadWrite)? - person cabgef; 23.07.2009
comment
@mikeh: Это будет зависеть от того, как автор открыл файл, но попробовать стоит. Однако я бы не стал делать ничего, что зависело бы от абсолютной синхронизации с писателем. - person Jon Skeet; 24.07.2009

Можно ли использовать MemoryMappedFiles?

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

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

person Jorge Córdoba    schedule 23.07.2009

Я бы порекомендовал использовать каналы, они действуют точно так же, как файлы, за исключением потоковой передачи данных непосредственно между приложениями, даже если приложения работают на разных ПК (хотя на самом деле это вариант только в том случае, если вы можете изменить оба приложения). Проверьте это в пространстве имен «System.IO.Pipes».

P.S. Для этого вы должны использовать «именованный» канал (каналы также поддерживаются в «c», поэтому в основном любой полуприличный язык программирования должен иметь возможность их реализовать)

person Grant Peters    schedule 23.07.2009

Я думаю, что (а) лучше, потому что:

  • Текущая позиция будет увеличиваться по мере чтения, и вам не нужно беспокоиться о ее сохранении;
  • Вам не нужно открывать его и искать нужную позицию (повторное открытие не должно быть намного медленнее, но сохранение его открытым дает ОС некоторые подсказки для оптимизации, я полагаю) каждый раз, когда вы его опрашиваете;
  • Другие решения, которые я могу придумать, требуют PInvokes для системных примитивов межпроцессной синхронизации. И они не будут быстрее, чем файловые операции уже во фреймворке.

Вам просто нужно установить правильные флаги FileShare:

Просто, например:

Сервер:

using(var writer = new BinaryWriter(new FileStream(@"D:\testlog.log", FileMode.Append, FileAccess.Write, FileShare.Read)))
{
    int n;
    while(Int32.TryParse(Console.ReadLine(), out n))
    {
        writer.Write(n);
        writer.Flush(); // write cached bytes to file
    }
}

Клиент:

using (var reader = new BinaryReader(new FileStream(@"D:\testlog.log", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
    string s;
    while (Console.ReadLine() != "exit")
    {
        // allocate buffer for new ints
        Int32[] buffer = new Int32[(reader.BaseStream.Length - reader.BaseStream.Position) / sizeof(Int32)];

        Console.WriteLine("Stream length: {0}", reader.BaseStream.Length);
        Console.Write("Ints read: ");
        for (int i = 0; i < buffer.Length; i++)
        {
            buffer[i] = reader.ReadInt32();
            Console.Write((i == 0 ? "" : ", ") + buffer[i].ToString());
        }
        Console.WriteLine();
    }
}
person Regent    schedule 23.07.2009

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

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

person Phill    schedule 23.07.2009