Announcement

Collapse
No announcement yet.

ADOConnection + TThread

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • ADOConnection + TThread

    Hallo <br>
    Wenn bei einer Anwendung in mehreren Threads eine Connection zur selben Datenbank stattfinden soll, muss dann in jedem Tread ein ADOConnections Objekt instanziert werden, oder könnte man mit einem für alle Treads auskommen?

    Helmut

  • #2
    Hallo,

    hinter <i>ADO Express</i> verbergen sich nur VCL-Wrapperkomponenten für die originalen COM-Objekte von Microsoft. Somit gelten die Regeln, die der COM-Standard (Component Object Model) aufstellt. Bevor aus einem Thread heraus auf ein COM-Objekt (wie die ADO-Objekte <i>Connection</i> oder <i>RecordSet</i>) zugegriffen werden darf, muss das Programm für diesen Thread ein <b>Apartment</b> anmelden (<i>CoInitialize</i>). Ein Zugriff auf dieses Objekt darf <b>nur dann</b> aus einem anderen Thread heraus erfolgen, wenn ein <b>Marshaler</b> den Interface-Zeiger speziell vorbereitet hat (Win32-API-Funktion <i>CoMarshalInterThreadInterfaceInStream</i>). Allerdings "lieben" alle ADO-Objekt das <b>STA</b> (Single-Thread-Apartment), so dass jeder Thread auf seine eigenen COM-Objektinstanzen direkt aus dem eigenen Apartment zugreifen sollte.

    Da ADO automatisch den Datenbankverbindungs-Pool verwendet, hätte es auch gar keine Vorteile, wenn jeder Thread auf ein gemeinsames Connection-Objekt zugreifen würde. Statt dessen sollte jeder Thread eine neue TADOConnection-Instanz erzeugen, die Verbindung aufbauen und am Ende alles wieder abräumen. Alle Datenbankaufrufe werden von ADO (genauer gesagt von OLE DB) abgefangen und auf den Datenbankverbindungs-Pool umgeleitet. Somit hat der ständige Verbindungsaufbau zur Datenbank keine Performance-Nachteile.

    Das Ganze könnte in einem Beispiel so aussehen, wobei der Thread auf ADO Express verzichtet und direkt auf die COM-Objekte von ADO zugreift. Die Vorgehensweise wäre mit ADO Exress jedoch die Gleiche:
    <pre>
    unit ADOThreadThread;

    interface

    uses
    Classes, Windows;

    type
    ADOSelectThread = class(TThread)
    private
    { Private-Deklarationen }
    FFrmWnd : HWND;
    FConnStr : String;
    FOrt : String;
    protected
    procedure Execute; override;
    public
    constructor Create(hFrmWnd : HWND; sConnStr, sOrt: String);
    end;

    implementation

    uses ADOInt, ActiveX, SysUtils, ADOThreadFrm;

    { ADOSelectThread }

    constructor ADOSelectThread.Create(hFrmWnd: HWND; sConnStr, sOrt: String);
    begin
    FFrmWnd := hFrmWnd;
    FConnStr := sConnStr;
    FOrt := sOrt;
    FreeOnTerminate := True;
    inherited Create(False);
    end;

    procedure ADOSelectThread.Execute;
    var
    aCN : _Connection;
    aRS : _RecordSet;
    vDummy : OleVariant;
    sCount : String;
    begin
    CoInitialize(nil);
    try
    aCN := CoConnection.Create;
    // Datenbankverbindungs-Pool von ADO, daher keine Nachteile,
    // wenn bei jedem Start die Datenbankverbindung neu geöffnet wird
    aCN.Open(FConnStr, '', '', adConnectUnspecified);
    aCN.Execute('SET LOCK_TIMEOUT 500', vDummy, adExecuteNoRecords);
    aRS := CoRecordSet.Create;
    aRS.CursorLocation := adUseClient;
    aRS.CursorType := adOpenForwardOnly;
    aRS.LockType := adLockReadOnly;
    aRS.Open('SELECT COUNT(*) FROM KUNDEN WHERE ORT = ' + QuotedStr(FOrt),
    aCN, adOpenStatic, adLockReadOnly, adCmdText);
    sCount := IntToStr(aRS.Fields[0].Value);
    aRS.Close;
    aCN.Close;
    PostMessage(FFrmWnd, PM_LABELTEXT, 0, Integer(PChar(sCount)));
    finally
    CoUninitialize;
    end;
    end;

    end.
    </pre&gt

    Comment

    Working...
    X