Announcement

Collapse
No announcement yet.

Problem mit ADO und THREAD

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

  • Problem mit ADO und THREAD

    Hallo ich habe folgendes Problem zu lösen, ich muß Daten aus verschiedenen dBase-Dateien in eine Interbasedatenbank importieren. Dazu habe ich mir ein Filterprogramm geschrieben. Aus der IBTabelle (Filter) werden für jeden Importfilter die Parameter geladen, Import Typ (dBase, Access, Excel ..), der jeweilige Pfad und die entsprechende Tabelle und eine SQL-Anweisung für die Auswahl bestimmter Daten. Daraus wird dann jeweils eine entsprechende ADOConnection sowie eine ADOQuery konfiguriert.
    Nun soll in regelmäßigen abständen die Filterdatei durchlaufen werden, und
    wenn eine neue Version einer Datei vorhanden ist (Dateidatum), dann soll dieser Filter aktievert werden und die Daten importiert.
    Um beim Importlauf den jeweiligen Rechner nicht zu blockieren, können schon mal einige 100000 Datensätze sein, habe ich die ganze Sache in einem
    Thread verpackt. Läuft alles wunderbar, nur wenn Sie am Importfilter die Datenquelle - spriche eine SQL-Anweisung auf die nächste dBase-Datei ausgeführt werden soll, bleibt das ergebnis leer. Auch wenn der Durchgang
    abgelaufen ist, kann ich auf die ADOQuery nicht mehr zugreifen. Erst wenn ich das Programm neu starte funktioniert es wieder.

    // ************************************************** *********** //
    // *** Thread-Anweisungen *** //
    // ************************************************** *********** //

    constructor TDBThread.Create(xZielDB:TIBDatabase;xTA:TIBTransa ction;xQuellDB:TADOConnection;
    xFilterTbl:TIBTable;xQuellSQL:TADOQuery;xZielTbl:T IBTable;xFilterID:Integer);
    begin
    inherited Create(True);

    FTA:= xTA; // Transaktion erstellen
    FDBZiel:=xZielDB; // Datenbankverbindung herstellen
    FDBQuelle:=xQuellDB; // Ado-Connection herstellen

    FFilter:=xFilterTbl; // Filtertabellen definieren und Filter suchen
    with FFilter do begin
    Database:=FDBZiel;
    TableName:='AFILTER';
    Active:=True;
    end;

    FQuelle:=xQuellSQL; // Quelltabelle festlegen
    FZiel:=xZielTbl; // Zieltabelle festlegen

    FProgress:=0;
    MaxAnz :=0;
    Start :=0;

    FreeOnTerminate:=True;
    Resume;

    end;

    procedure TDBThread.Execute;
    begin
    CoInitialize(nil);
    Try
    Synchronize(ShowUpdateEin);
    StartUpdate;
    finally
    CoUninitialize;
    Synchronize(ShowUpdateAus);
    end;
    end;

    procedure TDBThread.StartUpdate;
    var xID:integer;
    begin
    // Durchlauf der einzelnen Filter;
    FFilter.First;
    for xID:=1 to FFilter.RecordCount do
    begin
    SetFilterParameter;
    Synchronize(ShowProgress);
    SchreibeDaten;
    FFilter.Next;
    end;
    end;

    // hier sollen nachher die Daten in die IB-Tabelle geschrieben werden
    procedure TDBThread.SchreibeDaten;
    begin
    FProgress:=0;
    MaxAnz := FQuelle.RecordCount;
    Start :=0;
    FQuelle.First;
    while not FQuelle.Eof do
    begin
    FProgress:=Round(Start/((MaxAnz-1)/100));
    Start:=Start+1;
    Synchronize(ShowProgress);
    FQuelle.Next;
    end;
    end;

    // hier wird zu jemdem Filter die Datenbankeigenschaften gesetzt
    procedure TDBThread.SetFilterParameter;
    var xListSQL:TStringList;
    begin
    xListSQL:=TStringList.Create;

    // Quelldatenbank definieren
    with FDBQuelle do begin
    FDBQuelle.Connected:=false;
    FDBQuelle.Provider:='Microsoft.Jet.OLEDB.4.0';
    FDBQuelle.Properties['Data Source'].Value :=FFilter.FieldByName('IMPORTDATEI').Value;
    case FFilter.FieldByName('IMPROTTYP').Value of
    2: Properties['Extended Properties'].Value := 'dBASE III';
    3: Properties['Extended Properties'].Value := 'Excel 8.0';
    end;
    FDBQuelle.LoginPrompt:=False;
    Connected:=True;
    end;

    // Quelltabelle festlegen
    with FQuelle do begin
    Close;
    Connection:=FDBQuelle;
    xListSQL.Assign(FFilter.FieldByName('SQLABFRAGE')) ;
    SQL:=xListSQL;
    Open;

  • #2
    Hallo,

    ADO ist völlig threadfest und ich bin bisher in meinen Projekten an dieser Stelle nicht auf Probleme gestossen. Allerdings gehe ich hier meistens anders vor, indem ich direkt im Thread nur mit den nativen ADO-Objekten Connection, Command und Recordset hantiere.

    Die ADO-Connection darf erst innerhalb des Threads (Execute) aufgebaut werden und muss innerhalb des gleichen Threads (Execute) wieder geschlossen werden. Das könnte zum Beispiel so aussehen:
    <pre>
    procedure TSoundexNameDiff.Execute;
    var
    aConnection : _Connection;
    aCommand : _Command;
    aParam : _Parameter;
    vName : OleVariant;
    vRowsAffected: OleVariant;
    iCount : Integer;
    begin
    vName := FName;
    CoInitialize(nil);
    try
    aConnection := CoConnection.Create;
    aConnection.Open(FCS, '', '', 0);
    try
    aCommand := CoCommand.Create;
    try
    aCommand.CommandType := adCmdStoredProc;
    aCommand.Set_CommandText('stprSoundexName1Diff');
    // Rückgabewert
    aParam := aCommand.CreateParameter('RETURN_VALUE', adInteger,
    adParamReturnValue,4, EmptyParam);
    aCommand.Parameters.Append(aParam);
    // Input-Parameter: Zu suchender Name
    aParam := aCommand.CreateParameter('@sNAME1', adVarChar,
    adParamInput, 40, vName);
    aCommand.Parameters.Append(aParam);
    // Ausführen
    aCommand.Set_ActiveConnection(aConnection);
    aCommand.Execute(vRowsAffected, EmptyParam, adExecuteNoRecords);
    iCount := aCommand.Parameters.Item[0].Value;
    if iCount > 0 then
    Synchronize(InfoUser);
    finally
    aParam := nil;
    aCommand := nil;
    end;
    finally
    aConnection.Close;
    aConnection := nil;
    end;
    finally
    CoUninitialize;
    end;
    // akustische Rückmeldung über das Thread-Ende
    MessageBeep($FFFFFFFF);
    end;
    </pre&gt

    Comment


    • #3
      Noch eine Anmerkung für die Problematik mit den Threads:

      Ich hatte das Problem, daß ich zwar in einer Unit alle meine Datenbankaufrufe gekapselt hatte, aber nach Umstellung auf ADO die berüchtigte "CoInitialize wurde nicht aufgerufen"-Problematik hatte.
      Das ganze lief zudem noch in einem Service ab -> großes Rätselraten.

      Es gibt durchaus eine Möglichkeit, in einem Thread ein ADOConnection-Objekt zu instanzieren, auf daß andere Threads zugreifen. Der Trick liegt in folgendem Aufruf:

      CoInitializeEx(nil,COINIT_MULTITHREADED);

      Damit teilt man der COM-Schnittstelle mit, daß andere Threads auch auf das Objekt müssen. Allerdings muss der Aufruf in dem Thread erfolgen, der anschließend das Objekt erzeugt.

      <pre>
      HRESULT CoInitializeEx(

      void * pvReserved, //Reserved

      DWORD dwCoInit //COINIT value

      );
      </pre>

      <b>Parameters</b>
      pvReserved
      [in] Reserved; must be NULL.

      dwCoInit

      [in] Flags specifying the concurrency model and initialization options for the thread. Values for this parameter are taken from the COINIT enumeration. Any combination of values from the COINIT enumeration can be used, except that the COINIT_APARTMENTTHREADED and COINIT_MULTITHREADED flags cannot both be set.

      <b>Return Values</b>
      This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following:
      S_OK

      The COM library was initialized successfully on the calling thread.

      S_FALSE

      The COM library is already initialized on the calling thread.

      RPC_E_CHANGED_MODE

      A previous call to CoInitializeEx specified a different concurrency model for the calling thread. If running Windows 2000, this could also mean that a change from neutral threaded apartment to single threaded apartment occurred.

      <i>
      Multi-threading (also called free-threading) allows calls to methods of objects created by this thread to be run on any thread. There is no serialization of calls – many calls may occur to the same method or to the same object or simultaneously. Multi-threaded object concurrency offers the highest performance and takes the best advantage of multi-processor hardware for cross-thread, cross-process, and cross-machine calling, since calls to objects are not serialized in any way. This means, however, that the code for objects must enforce its own concurrency model, typically through the use of Win32 synchronization primitives, such as critical sections, semaphores, or mutexes. In addition, because the object doesn't control the lifetime of the threads that are accessing it, no thread-specific state may be stored in the object (in Thread-Local-Storage).
      </i>

      Die Parameter sind so definiert:

      <pre>
      typedef enum tagCOINIT{

      COINIT_APARTMENTTHREADED = 0x2, // Apartment model

      COINIT_MULTITHREADED = 0x0, // OLE calls objects on any thread.

      COINIT_DISABLE_OLE1DDE = 0x4, // Don't use DDE for Ole1 support.

      COINIT_SPEED_OVER_MEMORY = 0x8, // Trade memory for speed.

      } COINIT;
      </pre>

      Hoffe, daß hilft jetzt einigen.

      Grüße
      Tim

      Comment


      • #4
        Hallo,

        ein Programm sollte nur dann ein MTA (Multi-threaded Apartment) beim Betriebssystem anmelden, wenn es zu 100% threadfest ist. Denn nur beim STA schaltet das Betriebssystem einen Serialisierungs-Weg dazwischen - beim MTA ist die eigene Anwendung völlig auf sich gestellt. Bevor ich in einem VCL-Programm MTA aktiviere, sollte das Ganze auf einem Dual-Prozessor-Rechner getestet werden. Dort machen sich alle Seiteneffekte nach kurzer Zeit bemerkbar, die auf einem Single-Prozessor-Rechner nur nach stundenlangen Programmläufen sporadisch nachweisbar sind.
        &#10

        Comment


        • #5
          Hallo,

          das Programm lief schon mehrere Monate multithreaded auf Multiprozessorsystemen, aber unter der BDE.
          Problem war dort: Wenn man in seltenen Fällen mal den Thread abschiessen musste, waren die Paradox-Tabellen hinüber, daher jetzt die ADO-Umstellung, in der Hoffnung, daß eine MDB stabiler ist.

          Eine MSDE unter ADO wäre noch wünschenswerter, aber damit hab ich noch keine Erfolge erzielen können.

          Alle Datenbankoperationen sind durch eine CriticalSection von einander getrennt, so daß dieses kein Problem darstellt.

          Grüße
          Tim

          Comment

          Working...
          X