![]() |
Datenbank: MS SQL Server • Version: 2008 • Zugriff über: ADO
Daten sammeln und in MS SQL-Datenbank hinzufügen???
Hallo!
Ich habe in meinem Programm mehrere Threads, die permanent laufen und XML-Daten liefern. Diese Daten muss ich einer MS SQL-Datenbank hinzufügen. Da es sich beim Datenbankserver um einen Remoteserver handelt und das direkte Hinzufügen im selben Thread desen Ablauf verzögern könnte (was ich in meinem Fall unbedingt vermeiden muss), habe ich einen anderen Thread erstellt, der die Daten einsammelt und der DB hinzufügt. Dafür habe ich zusätzlich noch eine TStringList erstellt, in der die für DB bestimmte Daten landen. Alle Threads fügen die XML-Daten dieser TStringList hinzu. Der DB-Thread greift immer wieder auf diese Liste zu, prüft ob neue Daten vorhanden sind und wenn was neues gibt, leitet die Daten an die DB weiter. Problem: Einige Datensätze aus der TStringList landen doppelt in die Datenbenk, während einige gar nicht hinzufügt werden. Dabei konnte ich sicherstellen, dass es keine gleiche Daten in der TStringList gibt. Was mache ich falsch? Liste für die Daten und CriticalSection:
Delphi-Quellcode:
So werden die Daten in die Liste hinzufügt:
...
var lstDBData: TStringList; csDBData: TCriticalSection;
Delphi-Quellcode:
Thread, der die Daten verarbeitet:
...
csDBData.Enter; try lstDBData.Add(XMLData); finally csDBData.Leave; end;
Delphi-Quellcode:
type
TAddDataToDB = class(TThread) constructor Create(CreateSuspended: Boolean; AAllowUpdate: Boolean); private AllowUpdate: Boolean; protected procedure Execute; override; end; constructor TAddDataToDB.Create(CreateSuspended: Boolean; AAllowUpdate: Boolean); begin inherited Create(CreateSuspended); FreeOnTerminate := true; AllowUpdate := AAllowUpdate; end; procedure TAddDataToDB.Execute; var ADO: TADOTable; XMLDoc: IXMLDocument; XMLRoot: IXMLNode; begin CoInitialize(nil); ADO := TADOTable.Create(nil); ADO.ConnectionString := 'Provider=SQLOLEDB.1;Password=' + DB_PASSWORD + ';' + 'Persist Security Info=True;User ID=' + DB_USERNAME + ';' + 'Initial Catalog=' + DB_NAME + ';' + 'Data Source=' + DB_HOST; ADO.TableName := DB_TABLE; while not Terminated do begin if lstDBData.Count > 0 then begin ADO.Active := true; repeat if Length(lstDBData.Strings[0]) > 0 then begin //Hier wird XML analysieret, zerteilt und in der DB hinzufügt end; //Verarbeite Daten aus der Liste entfernen csDBData.Enter; try lstDBData.Delete(0); finally csDBData.Leave; end; until lstDBData.Count = 0; ADO.Active := false; end; Sleep(10); end; ADO.Free; end; |
Re: Daten sammeln und in MS SQL-Datenbank hinzufügen???
hallo romber,
hast du auch sichergestellt, mit einer critical section, wenn die daten an den mssql server ge-insert-ed werden, das kein anderer die stringlist manipulieren kann. es könnte ja sein das du den insert sql mit params erstellst. genau in der zeit schiebt ein anderer thread ein neues elemnt in die liste. somit würde es erklären das dir elemente verloren gehen. auch die doppelten könnte man produzieren in dem erstmal das neue element in die db gebracht wird und nun ein neues kommt und der insert thread loslegt aber der neue eintrag noch nicht im stringlist ist da er gerade noch im reinschreiben ist. ich vermute du hast nur eine critical section beim add in die stringlist. jedoch du musst auch beim löschen und herauslesen eine critical section einbauen. am besten benutzt du erstmal die gleiche damit alles erstmal serialisiert ist. dann müsste es laufen. jedoch würde ich das problem etwas anders lösen. die thread zum einsammeln kannst du lassen. diese können es auch in ein TStrings(list) reinpusten. jedoch würde ich dann auf den event itemsadd erweitern und dort den string mit übergeben. in diesem event wird dann der insert in die mssql server db ausgeführt. beim löschen des inhaltes musst du aber auch wieder die critical section nutzen. also summary: bei jeder manipulation deiner TStriglist solltest du CriticalSection sauber erstellen und verlassen. gruss daniel m |
Re: Daten sammeln und in MS SQL-Datenbank hinzufügen???
Anfügen und 'Nächsten Job holen' müssen geschützt werden. Am Besten Du spendierst deinem Thread zwei Methoden:
Delphi-Quellcode:
Das klassische Designpattern eines Workerthreads.
Type
TDBThread ... Private FCS : TCriticalSection; Function GetNextJob :String; Public Procedure AddJob (Const aJob : String; ... End; Function TDBThread.GetNextJob :String; Begin fCS.Enter; Try If fJobList.Count>0 Then Begin Result := fJobList[0]; fJobList.Delete(0) End Else Result := ''; Finally fCS.Lave; End End; Procedure TDBThread.AddJob (Const aJob : String; Begin fCS.Enter; Try fJobList.Add(aJob); // Ich signalisiere hier der Execute-Methode per Release (fDataAvailSemaphore, 1, nil); // das ein Job zur Verarbeitung bereit steht. // Die Semaphore wird mit // fDataAvailSemaphore := CreateSemaphore (nil,0,maxint,nil); // im Konstruktor initialisiert Finally fCS.Lave; End End; Procedure TDBThread.Execute; Begin CoInitialize(); // Create ADO-Connection While not Terminated do Begin WaitForSingleObject (fDataAvailSemaphore , INFINITE); If Not Terminated Then ExecuteJob (GetNextJob); End; CoUninitialize(); End; ![]() |
Re: Daten sammeln und in MS SQL-Datenbank hinzufügen???
Ich meine mich zu erinnern, dass Bernd Ua mal berichtet hat, dass die TADO* Komponenten nicht Multithreading fähig sind.
Vielleicht forscht du mal in diese Richtung. Aber sicher bin ich mir nicht. |
Re: Daten sammeln und in MS SQL-Datenbank hinzufügen???
Vielen Dank für Eure Beiträge! :cheers:
Ich konnte das Problem inzwischen lösen, es war ein Fehler in der Funktion für die XML-Analyse. Wegen fehlerhaftger Verarbeitung von Exception wurde der genze Thread-Ablauf durcheinander. Um das Ganze dann noch sicherer zu machen, habe ich auch die hilfreichen Vorschläge von dmagin und alzaimar umgesetzt. Zitat:
Besten Dank an alzaimar für die Beispielcode und den Link zum Tutorial! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:23 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz