|
![]() |
|
Registriert seit: 27. Apr 2006 Ort: München 556 Beiträge Delphi 7 Professional |
#1
Hallo erstmal,
Einführung ich hab vor ca. nem Jahr mal das Problem, dass ich einen Datenstrom in einem Puffer zwischenspeichern musste, damit der Puffer von einem anderen Stream ausgelesen und verarbeitet werden konnte. Da ich damals zu faul war, mir was zusammenzusuchen, hab ich eben schnell eine kleine Komponente geschrieben, die das erledigt. Da ich damals mehrere Threads hatte, ist die Komponente fast Thread-Safe. Das "Fast" ist nur ein kleines Fast: wenn man dem Puffer eine neue Puffer-Größe zuweisen will, wird eine Routine aufgerufen, die nicht Thread-Safe ist. Da man aber den Puffer nur einmal mit einer fixen Größe erstellen sollte, hab ich die Routine absichtlich Thread-UNsafe gelassen. eine Anwendungsmöglichkeit Streams: Wenn man z.B. eine menge einzelner Bytes in eine Datei schreiben will, ist es nicht immer sinnvoll, immer wieder Stream.Write(aByte, 1); aufzurufen. Dies könnte man duch den Puffer sinnvoller gestallten:
Delphi-Quellcode:
Zwar ist die Routine mit dem Puffer etwas länger, aber mann könnte z.B. das Schreiben in den eigentlichen Stream in einen Thread verlegen, damit das Hauptprogramm nicht vom Schreiben in den Stream angehalten wird (z.B. bei langsamen Disketten-Laufwerken
aFiFoBuffer.Write(aByte, 1);
if aFifoBuffer.GetFilledSize > 1024 then // wenn mehr als 1KB Daten gespeichert sind begin GetMem(Buffer, aFiFoBuffer.GetFilledSize); try aReaded := aFiFoBuffer.Read(Buffer^, aFiFoBufferSize); Stream.Write(Buffer^, aReaded); finally FreeMem(Buffer); end; end; ![]() Auch komplexe Sachen wie einen Schreibpuffer für ein Brenn-Programm könnten damit erstellt werden. Benutzung Ich habe in den ersten Zeilen eine Beispielroutine hinzugefügt. Diese kann einfach in eine Button-Click-Methode eingefügt werden. Man benötigt dazu nur eine Form mit zwei Edits ("Edit1" und "Edit") eine Memo-Komponenten ("Memo1") und natürlich einen Button, der das Ganze dann ausführt. Funktionsweise anhand eines Beispiels Ich will kurz die Funktionsweise anhand eines einfachen Beispiels zeigen.
Code:
Start:
Legende:
. : freier Puffer-Speicher (das muss nicht heißen, dass die Werte alle gleich 0 sind. Es kann sich alles mögliche hier befinden.) | : - über dem Puffer: Schreibkopfposition - unter dem Puffer: Lesekopfposition
Code:
Schritt 1: speichere "Hallo Welt!"
(nach dem Start)
| ............................................ (der Puffer) |
Code:
Schritt 2: speichere "Mir gehts gut!"
(nach dem Schreiben)
| Hallo Welt!................................. (der Puffer) |
Code:
Schritt 3: lese den ersten String aus (Ergebnis: "Hallo Welt!")
(nach dem Schreiben)
| Hallo Welt!Mir gehts gut!................... (der Puffer) |
Code:
Schritt 4: speichere "Das ist aber eine tolle Sache" (Der Buffer ist jetzt komplett voll)
(nach dem Lesen)
| ...........Mir gehts gut!................... (der Puffer) |
Code:
Schritt 5: lese den zweiten und dritten String aus
(nach dem Schreiben)
| olle Sache.Mir gehts gut!Das ist aber eine t (der Puffer) |
Code:
Der Puffer ist jetzt wieder leer (obwohl die Lese und Schreibposition nicht = 0 sind)
(nach den Lese-Vorgängen)
| olle Sache...............Das ist aber eine t (der Puffer) | | ............................................ (der Puffer) | Und was noch? Das wars schon wieder von meiner Seite. Vielleicht kann dies ja der ein oder andere brauchen Und hier ist die Unit:
Delphi-Quellcode:
[edit]nen Rechtschreibfehler ausgebessert
unit FiFoBuffer;
{******************************************************************************* Type : FifoBuffer Filename : FifoBuffer.pas Date : 2008-03-19 Version : 1.0.0.0 Last modified : 2008-03-19 Author : David Huettig a.k.a littleDave URL : [url]www.godlikesoft.de[/url] Copyright : Copyright (c) 2008 David Huettig History : v. 1.0.0.0 ========== - Initial release *******************************************************************************} {******************************************************************************* Copyright (c) 2008, David Huettig ["copyright holder(s)"] 1. You are free to use this unit in any project, freeware or commercial. You are allowed to release this unit in OpenSource-projects - as long as you do not claim that you are the original author. 2. You are NOT allowed to remove these comment lines 3. If you redistribute this unit, please fill the history with any changes you made. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************} {******************************************************************************* Diese Unit erstellt einen FiFo-Puffer (FirstIn - FirstOut - Puffer). Dieser Puffer erlaubt es, sequentiell Daten in den Puffer zu schreiben um diese dann später IN DER GLEICHEN REIHENFOLGE auslesen zu können. Die größe des Puffers ist fix und wird am Anfang festgelegt. Es können nur soviel Bytes im Puffer gespeichert werden, wie die maximale Größe. Dieser Puffer ist bis auf die Funktion "AllocateMemory", die durch die Property TotalSize := xxx aufgerufen wird, thread-save. Das heißt, es kann ohne Probleme mit Threads gearbeitet werden, ohne CriticalSections oder Synchronizes zu benutzen. Dies wird alles intern geregelt! =============================================== = Kurzes Beispiel zur Benutzung = =============================================== var Buffer: TFiFoBuffer; // Das Puffer-Object len : integer; // Daten die gelesen / geschrieben werden sollen s : string; // Daten die gelesen / geschrieben werden sollen begin Buffer := TFiFoBuffer.Create(1024*5); // Puffer mit einer Füllmenge von 5KB erstellen try s := Edit1.Text; // Den ersten Text auslesen len := length(s); // die Länge des Textes auslesen if Buffer.Write(len, SizeOf(len)) = -1 then // Die Länge des Textes speichern raise Exception.Create('Fehler beim Schreiben - Puffer zu klein'); if Buffer.Write(s[1], len) = -1 then // Den Text an sich speichern raise Exception.Create('Fehler beim Schreiben - Puffer zu klein'); s := Edit2.Text; // Dann den zweiten Text auslesen len := length(s); // die Länge des Textes auslesen if Buffer.Write(len, SizeOf(len)) = -1 then // Die Länge des Textes speichern raise Exception.Create('Fehler beim Schreiben - Puffer zu klein'); if Buffer.Write(s[1], len) = -1 then // Den Text an sich speichern raise Exception.Create('Fehler beim Schreiben - Puffer zu klein'); (* Jetzt stehen beide Strings im Puffer - jetzt kann ich sie auslesen *) if Buffer.Read(len, SizeOf(len)) = -1 then // Zuerst die Länge auslesen raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer'); SetLength(s, len); // Den String initialisieren if Buffer.Read(s[1], len) = -1 then // Den String auslesen raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer'); (* Jetzt steht in s wieder der Inhalt von Edit1 *) Memo1.Lines.Add(s); // String ausgeben if Buffer.Read(len, SizeOf(len)) = -1 then // Zuerst die Länge auslesen raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer'); SetLength(s, len); // Den String initialisieren if Buffer.Read(s[1], len) = -1 then // Den String auslesen raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer'); (* Jetzt steht in s wieder der Inhalt von Edit2 *) Memo1.Lines.Add(s); // String ausgeben finally Buffer.Free; end; end; *******************************************************************************} interface uses SyncObjs; type TFiFoBuffer = class(TObject) private FData : Pointer; FSize : integer; FReadPointer : integer; FWritePointer : integer; FWriteSection : TCriticalSection; FReadSection : TCriticalSection; protected function GetFilledSize : integer; procedure AllocateMemory(newSize: integer); public constructor Create(Size: integer); destructor Destroy; override; procedure ResetBuffer; function Write(const Buffer; Count: integer): integer; function Read(var Buffer; Count: integer): integer; property TotalSize : integer read FSize write AllocateMemory; property FilledSize : integer read GetFilledSize; end; implementation { TFiFoBuffer } procedure TFiFoBuffer.AllocateMemory(newSize: integer); begin if newSize < 10 then // 10 Bytes sind minimum - könnten theoretisch auch 2 sein, doch wer braucht newSize := 10; // schon einen Buffer mit 2 Byte if Assigned(FData) then // Ist bereits ein Puffer vorhanden FreeMem(FData, FSize); // Müssen wir ihn erst freigeben GetMem(FData, newSize); // Speicher reservieren FSize := newSize; // Größe speichern ResetBuffer; // Lese- und Schreib "Kopf" zurücksetzen end; constructor TFiFoBuffer.Create(Size: integer); begin inherited Create; FReadSection := TCriticalSection.Create; FWriteSection := TCriticalSection.Create; AllocateMemory(Size); end; destructor TFiFoBuffer.Destroy; begin if Assigned(FData) then FreeMem(FData, FSize); FWriteSection.Free; FReadSection.Free; inherited; end; function TFiFoBuffer.GetFilledSize: integer; begin FWriteSection.Enter; // Benötige Zugriff auf FWritePointer FReadSection.Enter; // Benötige Zugriff auf FReadPointer try if (FReadPointer > FWritePointer) then result := FSize - FReadPointer + FWritePointer // Puffer hat einen Rap-Around else result := FWritePointer - FReadPointer; finally FReadSection.Leave; // Und die kritischen Bereiche wieder verlassen FWriteSection.Leave; end; end; function TFiFoBuffer.Read(var Buffer; Count: integer): integer; var Address : Pointer; SplitSize : integer; begin result := -1; // Ergebniss initialisieren if GetFilledSize < Count then exit; // Es soll mehr gelesen werden, als im Puffer drinnsteht - abbrechen FReadSection.Enter; // Benötige Zugriff auf FReadPointer try if FSize - FReadPointer < Count then // Daten sind gesplittet begin SplitSize := FSize - FReadPointer; Move(Pointer(LongInt(FData) + FReadPointer)^, Buffer , SplitSize); // Den hinteren Teil lesen FReadPointer := 0; Address := Addr(Buffer); Address := Pointer(LongInt(Address) + (SplitSize)); Move(FData^, Address^, Count - SplitSize); // Den vorderen Teil lesen inc(FReadPointer, Count - SplitSize); // Read Pointer anpassen result := Count; // Anzahl gelesener Bytes zurückgeben end else begin // Daten sind nicht gesplittet Move(Pointer(LongInt(FData) + FReadPointer)^, Buffer, Count); // Einfach fröhlich auslesen result := Count; // Anzahl der gelesenen Bytes zurückgeben inc(FReadPointer, Count); // Read-Position für den nächsten Zugriff anpassen end; if FReadPointer > FSize then // Falls doch ein Splitting erfolgt ist - anpassen FReadPointer := FReadPointer - FSize; finally FReadSection.Leave; // LesePointer wieder Freigeben end; end; procedure TFiFoBuffer.ResetBuffer; begin FReadSection.Enter; // Benötige Zugriff auf Lese-Pointer FWriteSection.Enter; // Benötige Zugriff auf Schreib-Pointer try FReadPointer := 0; // Positionen zurücksetzen FWritePointer := 0; finally FReadSection.Leave; // Und Zugriff wieder Freigeben FWriteSection.Leave; end; end; function TFiFoBuffer.Write(const Buffer; Count: integer): integer; var Address : Pointer; SplitSize : integer; begin result := -1; // Anzahl geschriebener Bytes initialisieren if Count < 1 then // Wer will schon 0 oder -5 byte schreiben exit; // Falls es jemanden gibt, bekommt er nichts if FSize - GetFilledSize < Count then // Falls nicht mehr genügend Platz ist exit; // Kann nicht mehr in den Puffer geschrieben werden - abbrechen FWriteSection.Enter; // Benötige Zugriff auf SchreibPointer try if FSize - FWritePointer < Count then // Daten müssen gesplittet werden begin SplitSize := FSize - FWritePointer; Address := Addr(Buffer); Move(Buffer, Pointer(LongInt(FData) + FWritePointer)^, SplitSize); // Ersten Block ans Ende schreiben FWritePointer := 0; Address := Pointer(LongInt(Address) + (SplitSize)); Move(Address^, FData^, Count - SplitSize); // Zweiten Block an den Anfang schreiben inc(FWritePointer, Count - SplitSize); // Write-Position setzen result := Count; // Anzahl geschrieber Bytes zurückgeben end else begin // Daten müssen nicht gesplittet werden Move(Buffer, Pointer(LongInt(FData) + FWritePointer)^, Count); // Fröhliches Kopieren result := Count; // Anzahl geschriebener Bytes zurückgeben inc(FWritePointer, Count); // Schrei-Position anpassen end; if FWritePointer > FSize then // Falls doch ein Splitting erfolgt ist - anpassen FWritePointer := FWritePointer - FSize; finally FWriteSection.Leave; // Und nun den Zugriff auf FWritePointer wieder freigeben end; end; end. ![]() |
![]() |
Dax
(Gast)
n/a Beiträge |
#2
Ein paar Tipps:
|
![]() |
Registriert seit: 27. Apr 2006 Ort: München 556 Beiträge Delphi 7 Professional |
#3
![]() Lasse verkleinern unter den Pufferfüllstand nicht zu und erhalte den Puffer beim vergößern (nicht unbedingt nötig, aber dann ließe sich der Puffer prima als Basis für Queues verwenden)
Wenn es jemand braucht, sollte er sich vielleicht selbst was dazu überlegen und den Source als kleine Anregung nehmen ![]() Da du beim Lesen/Schreiben sowieso immer beide Sections mindestens einmal brauchst, schnapp dir zu anfang beide und gib sie direkt nach Ende des Bedarfs wieder frei, das dürfte den Durchsatz ein wenig erhöhen.
|
![]() |
Dax
(Gast)
n/a Beiträge |
#4
![]() Das stimmt nicht ganz - beim Lesen brauch ich die Read-Section, beim Schreiben die Schreib-Section. Die unterschiedliche Objekte haben schon ihren sinn. Wäre ja sonst auch blödsinn, sonst wäre das MultiThreading total umsonst, da man sonst nur Schreiben könnte wenn nicht gerade gelesen wird - und umgekehrt
![]() |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |