Навигация
Главная
Поиск
Форум
FAQ's
Ссылки
Карта сайта
Чат программистов

Статьи
-Delphi
-C/C++
-Turbo Pascal
-Assembler
-Java/JS
-PHP
-Perl
-DHTML
-Prolog
-GPSS
-Сайтостроительство
-CMS: PHP Fusion
-Инвестирование

Файлы
-Для программистов
-Компонеты для Delphi
-Исходники на Delphi
-Исходники на C/C++
-Книги по Delphi
-Книги по С/С++
-Книги по JAVA/JS
-Книги по Basic/VB/.NET
-Книги по PHP/MySQL
-Книги по Assembler
-PHP Fusion MOD'ы
-by Kest
Professional Download System
Реклама
Услуги

Автоматическое добавление статей на сайты на Wordpress, Joomla, DLE
Заказать продвижение сайта
Программа для рисования блок-схем
Инженерный калькулятор онлайн
Таблица сложения онлайн
Популярные статьи
OpenGL и Delphi... 65535
Форум на вашем ... 65535
21 ошибка прогр... 65535
HACK F.A.Q 65535
Бип из системно... 65535
Гостевая книга ... 65535
Invision Power ... 65535
Пример работы с... 65535
Содержание сайт... 65535
ТЕХНОЛОГИИ ДОСТ... 65535
Организация зап... 65535
Вызов хранимых ... 65535
Создание отчето... 65535
Имитационное мо... 65535
Программируемая... 65535
Эмулятор микроп... 65535
Подключение Mic... 65535
Создание потоко... 65535
Приложение «Про... 65535
Оператор выбора... 65535
Реклама
Сейчас на сайте
Гостей: 11
На сайте нет зарегистрированных пользователей

Пользователей: 13,368
новичок: Goosprin
Новости
Реклама
Выполняем курсовые и лабораторные по разным языкам программирования
Подробнее - курсовые и лабораторные на заказ
Delphi, Turbo Pascal, Assembler, C, C++, C#, Visual Basic, Java, GPSS, Prolog, 3D MAX, Компас 3D
Заказать программу для Windows Mobile, Symbian

Моделирование вычислительного центра на GPSS + Отчет + Блок схема
База данных - словарь терминов на Delphi + Пояснительная записка
Моделирование интернет магазина (Apache, Php, Html) на GPSS + Блок схема

Буферы для потоков
Автор: Сергей Парунов

Стандартные потоки, широко применяющиеся в 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.
Опубликовал Kest November 13 2008 12:25:49 · 2 Комментариев · 10100 Прочтений · Для печати

• Не нашли ответ на свой вопрос? Тогда задайте вопрос в комментариях или на форуме! •


Комментарии
Ricky.1 October 13 2012 20:45:16
Можно поставить ссылку на codingrus.ru в своем форуме?
Kest October 14 2012 17:38:46
поставь ))
Добавить комментарий
Имя:



smiley smiley smiley smiley smiley smiley smiley smiley smiley
Запретить смайлики в комментариях

Введите проверочный код:* =
Рейтинги
Рейтинг доступен только для пользователей.

Пожалуйста, залогиньтесь или зарегистрируйтесь для голосования.

Нет данных для оценки.
Гость
Имя

Пароль



Вы не зарегистрированны?
Нажмите здесь для регистрации.

Забыли пароль?
Запросите новый здесь.
Поделиться ссылкой
Фолловь меня в Твиттере! • Смотрите канал о путешествияхКак приготовить мидии в тайланде?
Загрузки
Новые загрузки
iChat v.7.0 Final...
iComm v.6.1 - выв...
Visual Studio 200...
CodeGear RAD Stud...
Шаблон для новост...

Случайные загрузки
Язык программиров...
Программирование ...
Run
Основы Delphi
AddPage [Исходник...
Apollovcl61
Microsoft SQL Ser...
Прграммирование в...
Аватары в комме...
Цветной Grid
DirHTMLReportBuil...
Доступа к БД Fire...
DateEdit
Стелтинг Стивен, ...
Error mod
Delphi 2005 для .NET
PDJ_Anima
Rotolabel
Delphi7 Для профе...
Delphi 2005 для W...

Топ загрузок
Приложение Клие... 100774
Delphi 7 Enterp... 97833
Converter AMR<-... 20268
GPSS World Stud... 17014
Borland C++Buil... 14191
Borland Delphi ... 10291
Turbo Pascal fo... 7373
Калькулятор [Ис... 5984
Visual Studio 2... 5207
Microsoft SQL S... 3661
Случайные статьи
Группирование и уп...
Неправильный спосо...
Два хоста обменива...
Рекурсивное постро...
Функция GraphErrorMsg
Что делать с седьм...
Основные понятия О...
Какими способами м...
Фаза создания экзе...
Создание пользоват...
":" expected
Использование прог...
Игровые автоматы о...
Распределение конц...
Переустановка пров...
Подключенные и лок...
2.3. Операторы
Invalid FOR contro...
Еще три возможност...
Каналы STREAMS
Блоки с информацие...
Причинные КОДЫ
Промокоды Фонбет
MixColumn
Сделать ставки на ...
Статистика



Друзья сайта
Программы, игры


Полезно
В какую объединенную сеть входит классовая сеть? Суммирование маршрутов Занимают ли таблицы память маршрутизатора?