(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Буферы для потоков

Источник: codingrus
Kest

Стандартные потоки, широко применяющиеся в Delphi, резко упрощают повседневную работу с потоковыми данными. Но и у них есть недостаток. Дело в том, что в VCL потоки, и, главное, их базовый класс TStream, реализованы "в лоб": без всяких хитростей данные немедленно препровождаются по назначению (например, в файл). И такие операции занимают весьма значительное время (многие сотни машинных команд). Хорошо, если надо работать с "крупными" данными (килобайт и выше) - а если данные небольшие и разнообразные, замедление достигает 100 и более раз (на типе Char).

Стандартный способ ускорения подобных операций - работа с массивами элементов, вводя-выводя их в/из потока сразу. Но, во-первых, это значительно сложнее поэлементных операций, а во-вторых, если элементы имеют непостоянную длину, становится ещё сложнее. Делая небольшое отступление, замечу, что стандартная библиотека потокового ввода-вывода в большинстве реализаций C++ сделана не так - там потоки могут сами буферизовать передаваемые данные. Не понимаю, почему в Borland решили обойтись без этого. Единственное приходящее в голову объяснение - они твёрдо рассчитывали на "крупный" и "средний" обмен данными, который оптимально производить как раз без буферизации. Действительно, если посмотреть на C++ - сразу кружится голова от количества команд, необходимых для обслуживания буфера. Связано это с тем, что потоки могут попеременно читаться и писаться, а кроме того, одновременно использоваться многими потоками кода.

Ввиду этих проблем мной были написаны сравнительно простые буферные классы (работают на Delphi версий 4-5, должны работать и на последующих, а вот 3 версия уже не поддерживает перегрузку методов - в принципе, переписать и тут несложно), позволяющие производить буферизованный обмен с любыми потоками. В целях максимального ускорения работы классы эти, во-первых, не "thread-safe", а во-вторых, это два разных класса - для записи и для чтения - унаследованных от одного базового (кроме TObject, разумеется). Классы "пристёгиваются" к потоку (кстати, в C++ это делается практически так же) - и пользуются ими только для "крупного" обмена, осуществляя "мелкий" самостоятельно со своим буфером.

ByteArray = packed array of Byte;

psnAbstractStreamBuffer = class {
Абстрактный предок классов для БЫСТРОЙ (буферизованно: вся цепочка до
API-функций задействуется только при переполнении буфера, что даёт ускорение
на порядок для данных длиной несколько байт) и УДОБНОЙ (перегруженные методы
для разных типов данных позволяют не задавать их размер, хотя можно и так)
бинарной работы с потоками заданной структуры. Принцип действия прост:
накопление данных в буфере и сброс в поток - у буфера записи; чтение из
потока и раздача данных из буфера - у буфера чтения. О позиции потока буфер
не заботится - просто пишет или читает в текущей. А иначе будет монстр.
Опасно что-то делать с потоком (хотя кому это надо?), когда к нему
присоединён буфер, ведь буфер может переписать поток, прочитать устаревшие
данные или сделать это не там, где надо. Перед подобными операциями
сбрасывйте буфер методом Flush (при смене присоединённого потока (свойство
Stream) и разрушении буфера это делается автоматически). Это касается и
попеременной работы буферов чтения и записи с одним потоком... хотя зачем
тогда буфер - чтобы постоянно его сбрасывать и устанавливать позицию потока?
При ошибках чтения и записи возникают стандартные VCL-исключения EReadError
и EWriteError.}

private
FStream: TStream; {присоединённый поток}
FSize: Cardinal; {размер буфера}
FBuffer, {буфер}
FBufferEnd: PChar; {конец буфера (сразу за последним байтом) - понятно, что
вместе с FSize и FBuffer избыточно, но это повысит скорость и упростит код}
procedure SetStream(const Value: TStream);
protected
FCurrPos: PChar; {текущая позиция в буфере}
property Size: Cardinal read FSize;
property Buffer: PChar read FBuffer;
property BufferEnd: PChar read FBufferEnd;
constructor Create(const Stream: TStream; const Size: Cardinal);
public
property Stream: TStream read FStream write SetStream; {<> Nil !!!}
procedure Flush; virtual; abstract; {сброс}
destructor Destroy; override; {Stream разрушайте сами, если надо, ПОСЛЕ
разрушения буфера}
end;

psnStreamWriter = class(psnAbstractStreamBuffer)
public
constructor Create(
const Stream: TStream; {присоединённый поток, меняется свойством Stream}
const Size: Cardinal = 1024 {размер буфера}
);
procedure Flush; override;
procedure WriteBuffer(const Data; const Count: Cardinal); {Этот метод
не перегружен с Write, так как Delphi (4-5, во всяком случае) плохо выносит
перегруженные методы, когда один из них имеет бестиповые параметры: Code
Explorer сходит с ума, а Code Completion вообще хулиганит - самовольно
добавляет раздел Private и дублирует объявление метода (без overload!!!)
там, а потом ругается: мол, первый метод не был объявлен как overload).}
procedure Write(const Data: Byte); overload;
procedure Write(const Data: Word); overload;
procedure Write(const Data: LongWord); overload;
procedure Write(const Data: Integer); overload;
procedure Write(const Data: Single); overload;
procedure Write(const Data: Double); overload;
procedure Write(const Data: Extended); overload;
procedure Write(const Data: string); overload;
procedure Write(const Data: ByteArray); overload;
end;

psnStreamReader = class(psnAbstractStreamBuffer)
public
constructor Create(
const Stream: TStream; {присоединённый поток, меняется свойством Stream}
const Size: Cardinal = 1024 {размер буфера}
);
procedure Flush; override;
procedure ReadBuffer(out Data; const Count: Cardinal);
procedure Read(out Data: Byte); overload;
procedure Read(out Data: Word); overload;
procedure Read(out Data: LongWord); overload;
procedure Read(out Data: Integer); overload;
procedure Read(out Data: Single); overload;
procedure Read(out Data: Double); overload;
procedure Read(out Data: Extended); overload;
procedure Read(out Data: string); overload;
procedure Read(out Data: ByteArray); overload;
end;

Их методы WriteBuffer и ReadBuffer работают аналогично одноименным методам класса TStream, то есть они генерируют стандартные VCL-исключения EWriteError и EReadError при невозможности осуществления операции. Причина этого в том, что, в конце концов, вы должны знать формат своего файла, а не я :). Кроме того, если кто не знает, исключения ускоряют работу по сравнению с постоянной проверкой результата (если секция try...finally или try...except содержит цикл, а не наоборот).

EWriteError может возникнуть много позже того, как в буферный класс поступят первые "не вмещающиеся" данные (но до того, как будет разорвана связь буферного класса и потока!) - ведь они буферизуются. В большинстве случаев это не критично: если в поток не удалось записать, можно "тушить свет" - это серьёзная ошибка, и поток к дальнейшему употреблению всё равно непригоден.

В силу того, что "мелкий" обмен с потоками часто производится типизированно - например, чтение строк или чисел с плавающей точкой - классы дополнены перегруженными методами Write и Read для распространённых типов, позволяющими не раздувать исходный (и машинный) код, постоянно указывая размеры передаваемых данных. Эти методы настолько просты, что расширение их набора не представляет проблем - фактически они просто транслируются в вызовы WriteBuffer и ReadBuffer.

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

Ссылки по теме


 Распечатать »
 Правила публикации »
  Обсудить материал в конференции Embarcadero »
Написать редактору 
 Рекомендовать » Дата публикации: 27.07.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Delphi Professional Named User
Enterprise Connectors (1 Year term)
ReSharper - Commercial annual subscription
ESET Secure Authentication newsale for 5 user, лицензия на 1 год
СУБД Линтер Бастион. Серверная лицензия. 5 клиентских подключений
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Все о PHP и даже больше
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
Обсуждения в форумах
Пишу программы на заказ профессионально (3248)
Пишу программы на заказ на языках Pascal (численные методы, списки, деревья, прерывания) под...
 
Написание программ для микроконтроллеров AVR, PIC, ARM, STM32 (25)
Напишу любую программу на любом искусственном языке. Профессиональный программист. Основная...
 
Разработка устройств на микроконтроллерах (39)
Профессиональный программист. Основная специализация: МИКРОКОНТРОЛЛЕРЫ, АССЕМБЛЕР для любых...
 
Пишу программы на заказ для студентов (254)
Пишу для студентов на с, с++, паскаль в средах ms visual studio, qt, builder, borland c, delphi....
 
Ремонт холодильников (1)
Если нужно починить срочно холодильник в Харькове то советую обращаться в эту...
 
 
 



    
rambler's top100 Rambler's Top100