Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi DLL und Threads (https://www.delphipraxis.net/180042-dll-und-threads.html)

haentschman 18. Apr 2014 11:16

Delphi-Version: 5

DLL und Threads
 
Hallo alle...:hi:
...schöne Ostern wünsch ich. 8-)

Zur Frage:
Bekannt ist ja, daß eine DLL nur einmal je Prozeß geladen wird. Meine DLL exportiert eine Hand voll Prozeduren. Diese Prozeduren wiederum werden in verschiedenen parallel laufenden Threads verwendet.

Was ist zu beachten? Wo gehört LoadLibrary und FreeLibrary hin? Müssen die Threads synchronisiert auf die DLL Prozeduren zugreifen?

Danke für Info´s.

Sir Rufo 18. Apr 2014 11:29

AW: DLL und Threads
 
Denk beim Multi-Threading immer an folgendes:

Ich kann problemlos zu n Personen sprechen und die gesprochenen Worte kommen dort an.
Umgekehrt funktioniert das mit n>1 aber nicht.

Sorge einfach dafür, dass dieser umgekehrte Fall nicht eintreten kann und dein Code ist threadsafe.

haentschman 18. Apr 2014 11:43

AW: DLL und Threads
 
:( Dann ist mein Konzept für die Katz...

Hintergrund:
Die DLL parst einen HTML Response. Dieser Response wird in verschiedenen paralelen Threads(HTML Anfragen) abgeholt und soll im Thread geparst und das Ergebnis gespeichert werden. Wenn ich pro Thread die gleiche DLL laden könnte... Das soll ja mit Tricks glaube ich funktionieren. Ich guck mal...

Tipps sind willkommen. :P

DeddyH 18. Apr 2014 11:51

AW: DLL und Threads
 
Ohne jetzt großartig nachgedacht zu haben: was ist denn, wenn die DLL die Threads erzeugt? Mit CallBack-Parametern beim Aufruf müsste das doch funktionieren, oder?

haentschman 18. Apr 2014 11:56

AW: DLL und Threads
 
Du meinst innerhalb der DLL im Speicherkontext der DLL?

Hmmm... Denke ich mal drüber nach. :roll:
Der Thread hat ja die "Lade Schleife" als Aufgabe. Das hat eigentlich in der DLL nix verloren. Die soll ausschließlich den Response parsen und die jeweiligen "Connection Strings" für die Anfrage vorhalten.

Sir Rufo 18. Apr 2014 12:12

AW: DLL und Threads
 
Streich mal das DLL Geraffel aus deinen Überlegungen, denn in der DLL ist einfach nur Code ausgelagert.

Ob eine DLL threadsafe ist, hängt nicht davon ab, ob der Code in einer DLL ist, sondern ob der Code threadsafe ist.

Also kann man das aus der Überlegung auch rausnehmen.

Ich baue mal eben eine kleine Demo, um das zu veranschaulichen ... dauert einen kleinen Moment.

haentschman 18. Apr 2014 12:22

AW: DLL und Threads
 
Zitat:

Streich mal das DLL Geraffel aus deinen Überlegungen, denn in der DLL ist einfach nur Code ausgelagert.
... ja klar. Und dieser Code sollte, so die Idee, in mehreren Threads aufrufbar sein.

Nachtrag:
Im Prinzip funktioniert das auch. Ich bin auf die Suche gegangen weil die eine Funktion aus der DLL exakt 13 mal den Text korrekt parst und beim 14 mal mit einer Zugriffsverletzung in der DLL abschmiert. Deswegen dachte ich an einen Grundsatzfehler.

Diese:
Delphi-Quellcode:
procedure ParseDeviceParameters(const aText: PChar; Parameters: TDeviceParameterList); stdcall;
var
  aParameter: TDeviceParameter;
  I, PositionFirst, PositionCount, TempPosition: Integer;
  sl: TStringList;
begin
  sl:= TStringList.Create;
  try
    sl.Text:= aText;
    for I:= 0 to sl.Count - 1 do
    begin
      if AnsiStartsText('dvcRec[', sl[I]) then
      begin
        aParameter:= TDeviceParameter.Create;
        PositionFirst:= PosEx('(',sl[I],1) + 1;
        PositionCount:= PosEx(',',sl[I],PositionFirst) - PositionFirst;
        aParameter.ParameterID:= StrToInt(Copy(sl[I],PositionFirst,PositionCount));
        PositionFirst:= PosEx('"',sl[I],1) + 1;
        TempPosition:= PositionFirst;
        PositionCount:= PosEx('"',sl[I],TempPosition) - PositionFirst;
        aParameter.Name:= Copy(sl[I],PositionFirst,PositionCount);
        TempPosition:= TempPosition + PositionCount + 1;
        PositionFirst:= PosEx(',',sl[I],TempPosition) + 1;
        PositionCount:= PosEx(',',sl[I],PositionFirst) - PositionFirst;
        aParameter.GroupID:= StrToInt(Copy(sl[I],PositionFirst,PositionCount));
        TempPosition:= TempPosition + PositionCount + 2;
        PositionFirst:= PosEx('"',sl[I],TempPosition) + 1;
        PositionCount:= PosEx('"',sl[I],PositionFirst) - PositionFirst;
        aParameter.Measure:= Copy(sl[I],PositionFirst,PositionCount);
        if aParameter.Measure = ' ' then
        begin
          aParameter.ParameterType:= ptDigital;
        end
        else
        begin
          aParameter.ParameterType:= ptAnalog;
        end;
        aParameter.State:= dsLoaded;
        Parameters.Add(aParameter);
      end;
    end;
  finally
    sl.Free;
  end;
end;
Aufgerufen hier: (FParser ist das Objekt welches sich um das Laden der DLL kümmert und die exportierten Geschichten zur Verfügung stellt)
Delphi-Quellcode:
FParser.ParseDeviceParameters(PChar(FResponseText), FDeviceParameterList);
for DeviceParameter in FDeviceParameterList do
begin
  DeviceParameter.DeviceID := FCurrentDevice.ID;
end;
FDatabase.SaveList(FDeviceParameterList);
FDatabase.GetList(FDeviceParameterList, FCurrentDevice);
Zitat:

Erste Gelegenheit für Exception bei $771AC41F. Exception-Klasse EAccessViolation mit Meldung 'Zugriffsverletzung bei Adresse 08D2328B in Modul 'BLA_500_V1_2.dll'. Schreiben von Adresse 00000001'. Prozess DVAServer.exe (3640)

Sir Rufo 18. Apr 2014 12:51

AW: DLL und Threads
 
Parser-Interface:
Delphi-Quellcode:
unit Parser;

interface

type
  IParser = interface
    ['{7082CCBB-2680-4BC4-8B17-7FFE1D602A0A}']
    function Parse( const AString : string ) : TArray<string>;
  end;

implementation

end.
und ein simpler nicht threadsafe Parser:
Delphi-Quellcode:
unit SimpleParser;

interface

uses
  Classes,
  Parser;

type
  TParserState = procedure( AChar : Char ) of object;

  TSimpleParser = class( TInterfacedObject, IParser )
  private
    FState : TParserState;
    FBuffer : string;
    FTokens : TStrings;
    procedure StartState( AChar : Char );
    procedure TokenState( AChar : Char );
  public
    constructor Create;
    destructor Destroy; override;

    function Parse( const AString : string ) : TArray<string>;
  end;

implementation

{ TSimpleParser }

constructor TSimpleParser.Create;
begin
  inherited;
  FTokens := TStringList.Create;
end;

destructor TSimpleParser.Destroy;
begin
  FTokens.Free;
  inherited;
end;

function TSimpleParser.Parse( const AString : string ) : TArray<string>;
var
  LIdx : Integer;
begin
  FTokens.Clear;
  FState := StartState;
  for LIdx := 1 to Length( AString ) do
  begin
    FState( AString[LIdx] );
  end;
  Result := FTokens.ToStringArray;
end;

procedure TSimpleParser.StartState( AChar : Char );
begin
  case AChar of
    ' ' :
      ;
    ',' :
      ;
  else
    FState := TokenState;
    FState(AChar);
  end;
end;

procedure TSimpleParser.TokenState( AChar : Char );
begin
  case AChar of
    ',' :
      begin
        FTokens.Add( FBuffer );
        FBuffer := '';
        FState := StartState;
      end;
  else
    FBuffer := FBuffer + AChar;
  end;
end;

end.
den ich einfach wrappe um mit dem doch threadsafe arbeiten zu können
Delphi-Quellcode:
unit ThreadSafeParser;

interface

uses
  Parser;

type
  TThreadSafeParser = class( TInterfacedObject, IParser )
  public
    function Parse( const AString : string ) : TArray<string>;
  end;

implementation

uses
  SimpleParser;

{ TThreadSafeParser }

function TThreadSafeParser.Parse( const AString : string ) : TArray<string>;
var
  LParser : IParser;
begin
  // TSimpleParser ist nicht threadsafe, aber
  // hier wird bei jedem Aufruf eine eigene Instanz erzeugt, auf die niemand sonst zugreift
  // dadurch wird das jetzt threadsafe
  LParser := TSimpleParser.Create;
  Result := LParser.Parse( AString );
end;

end.
Delphi-Quellcode:
procedure Test;
var
  LParser : IParser;
begin
  LParser := TSimpleParser.Create;
  OutputParseResult( LParser.Parse( '1,2,3,4' ) );

  LParser := TThreadSafeParser.Create;
  OutputParseResult( LParser.Parse( '1,2,3,4' ) );
end;

Sir Rufo 18. Apr 2014 12:52

AW: DLL und Threads
 
Zitat:

Zitat von haentschman (Beitrag 1256144)
Aufgerufen hier: (FParser ist das Objekt welches sich um das Laden der DLL kümmert und die exportierten Geschichten zur Verfügung stellt)
Delphi-Quellcode:
FParser.ParseDeviceParameters(PChar(FResponseText), FDeviceParameterList);
for DeviceParameter in FDeviceParameterList do
begin
  DeviceParameter.DeviceID := FCurrentDevice.ID;
end;
FDatabase.SaveList(FDeviceParameterList);
FDatabase.GetList(FDeviceParameterList, FCurrentDevice);
Zitat:

Erste Gelegenheit für Exception bei $771AC41F. Exception-Klasse EAccessViolation mit Meldung 'Zugriffsverletzung bei Adresse 08D2328B in Modul 'BLA_500_V1_2.dll'. Schreiben von Adresse 00000001'. Prozess DVAServer.exe (3640)

Ist der Zugriff auf
Delphi-Quellcode:
FParser
,
Delphi-Quellcode:
FDatabase
auch insgesamt threadsafe?

haentschman 18. Apr 2014 12:59

AW: DLL und Threads
 
FParser und FDatabase sind jeweils im Create des Threads erzeugt und im Destroy freigegeben. Da sollte das gut sein... :zwinker: Der Thread soll völlig allein die Aufgabe abarbeiten (incl. Datenbankspeicherung). Der Parser hat zwar seine eigene Instanz im Thread aber dann das gleiche DLL Handle wie der nächste bzw. vorhergehende Thread.
Wenn ich die o.g. Funktion auskommentiere läuft das ganze und beendet sich ohne Speicherlöcher und Zugriffverletzungen. Nehme ich sie rein ist bei 13 Schluß. An dem HTML Response ist nix außergewöhnliches zu erkennen.

PS: Ich hatte auch schon an ein Interface gedacht Die DLL gibt ein Interface zurück und es wird nur mit dem Interface gearbeitet statt mit den Funktionen. Macht das einen Unterschied mit dem DLL Handling?

Warum DLL:
Es gibt geschätzt 30 verschiedene Geräteversionen die sich im Response unterscheiden. Die passende DLL (Parser) zur Geräteversion wird dynamisch geladen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:07 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz