Maulwurf_der_Schlaue Geschrieben 6. März 2008 Geschrieben 6. März 2008 Hallo liebe Community, ich habe ein Problem bei der Thread-Programmierung. Ich starte mehrere Threads, viele dieser gestarteten Threads melden sich ordnungsgemäß ab (über Synchronize). Doch es gibt einige schwarze Schafe welche diese "Bin-Fertig"-Routine nicht durchlaufen, obwohl es keinen Umweg gibt. Den Thread starte ich so: (* viel Code vorher *) (* Neuen Thread erzeugen und in Array merken*) AktivThreads[idxAktivThread] := TDownload.Create( (* Erster Parameter : Speicherort *) linkSaveName, (* Zweiter Parameter : Quelle *) link, (* Dritter Parameter : Zusätzlich Identifikations# da ThreadID doppelt vorkommt *) threadUID, (* Vierter Parameter : Create Suspended *) True ); (* viel Code nachher *) [/PHP] Der Thread selber sieht so aus: [PHP] unit Download; interface uses Classes, UrlMon; type TDownload = class(TThread) public constructor Create(pSpeichort: String; pLink: String; pUniqueID: String; CreateSuspended : BOOLEAN = TRUE); private protected procedure Execute; override; (* Synchronisierter Zugriff auf Hauptthread *) procedure AusListeEntfernen; var Speicherort: String; SourceFile: String; UniqueID: String; end; implementation (* Code ist das Hautpprogramm *) uses Code; (* Überladene Create-Funktion zur Variablen Übergabe *) constructor TDownload.Create(pSpeichort: String; pLink: String; pUniqueID: String; CreateSuspended : BOOLEAN = TRUE); begin Inherited Create(CreateSuspended); Speicherort := pSpeichort; SourceFile := pLink; UniqueID := pUniqueID; end; procedure TDownload.AusListeEntfernen(); begin Form1.ThreadEntferneAusListe(Self.ThreadID, UniqueID); end; procedure TDownload.Execute; begin try UrlDownloadToFile(nil, PChar(SourceFile), PChar(Speicherort), 0,nil); finally (* keine Aktion nötig, soll nur nicht abstürzen bei Fehler *) end; Synchronize(AusListeEntfernen); end; end. so und jetzt noch die Synchronisierte Funktion im Haupt-Thread: procedure TForm1.ThreadEntferneAusListe(ID: Cardinal; UID: String); var index : byte; ThreadFound : Boolean; begin ThreadFound := False; (* Array Durchlaufen und entsprechenden Thread finden *) for index := 1 to maxDownloadThreads do begin if (AktivThreads[index].ThreadID = ID) and (UniqueIDs[index] = UID) then begin AktivThreads[index] := nil; Protokoll.Lines.Add('Thread #' + IntToStr(index) + ' mit ID ' + IntToStr(ID) + ' beendet.'); (* weiterer kurzer Code um Ereignis sichtbar zu machen *) ThreadFound := True; end; end; if not ThreadFound then begin Protokoll.Lines.Add('**SPEICHERLECK** Thread' + ' mit ID ' + IntToStr(ID) + ' nicht gefunden!'); Sleep(500); end; end; [/PHP] Die Meldung mit dem Speicherleck hatte ich noch nie! Das heißt eigentlich das alles Threads immer Ordnungsgemäß funktionieren müssten, leider "verliert" das Programm Threads. Sie melden sich einfach nicht ab, obwohl er fertig sein müsste. Ich weiß nicht wie das Problem entsteht, vllt. habe ich einen Denkfehler oder etwas falsch implementiert. Ich hoffe Ihr könnt mir einen Tipp geben an welcher Stelle ich etwas besser machen kann/sollte/muss . Danke vorab! Zitieren
geloescht_JesterDay Geschrieben 7. März 2008 Geschrieben 7. März 2008 Hmmm, threads in Delphi sind schon eine Weile her... ich versuch es mal. Erstmal, was mit gleich aufgefallen ist: type TDownload = class(TThread) public constructor Create(pSpeichort: String; pLink: String; pUniqueID: String; CreateSuspended : BOOLEAN = TRUE); private protected procedure Execute; override; (* Synchronisierter Zugriff auf Hauptthread *) procedure AusListeEntfernen; var Speicherort: String; SourceFile: String; UniqueID: String; end; var im protected? Willst du deine Threads weitervererben? Oder die Variablen irgendwem sonst zugänglich machen? Denke doch nicht. Also solltest du die ganz normal als Felder im Private des threads deklarieren: type TDownload = class(TThread) public constructor Create(pSpeichort: String; pLink: String; pUniqueID: String; CreateSuspended : BOOLEAN = TRUE); private Speicherort: String; SourceFile: String; UniqueID: String; protected procedure Execute; override; (* Synchronisierter Zugriff auf Hauptthread *) procedure AusListeEntfernen; end; Und ohne var davor. Weiter die Einbindung von Form1. :eek Ich hab jetzt keine Delphi-Hilfe mehr da oder sonst was, aber sowas ist ein abolutes NoGo. Zum einen beziehst du dich direkt auf den Formnamen (wobei form1 auch nicht form1 heißen sollte, aber egal ). Wenn du den mal änderst? Oder wenn du die Threads in einem anderen Programm verwenden willst? Wenn du ein zweites Formulas hinzufügst, was dann den Threadaufruf startet? Der Sinn der modularen Programmierung ist ja, dass die Module (die einzelnen Units) recht frei verwendet werden können, auch in anderen Programmen. Genau das verhinderst du ja hier, weil du alles von deinem Hauptformular abhängig machst. Also: Die Prozedur AusListeEntfernen entfernen. Das uses Code entfernen. In der Objektorientierung gibt es etwas wunderbares, nämlich die Events. Ein TThread bietet auch ein paar Events, unter anderem das OnTerminate-Event. Das wird genau dann abgefeuert, wenn der Thread sich beendet. Das solltest du dann auch nutzen. Ich kenn die genaue Signatur des Events nicht mehr, aber die Hilfe sagt dir da ne Menge. (Hab ich schonmal gesagt, dass ich die Delphi Hilfe liebe? Ich hab noch keine Hilfe gesehen, die so ausführlich und gut ist wie die von Delphi. Weder Visual Studio, MSDN, noch Java oder ähnliches haben auch nur annähernd so eine gute Hilfe wie Delphi!) Falls du das noch nie gemacht hast hier eine kleine Erklärung: Einem Event kannst du eine Prozedur zuweisen, die dann ausgeführt wird, wenn das Event aufgerufen wird. Die Prozedur kann irgendeine sein, sie muss nur dieselben Parameter haben wie die geforderten. du kennst es vielleicht, wenn du einen Doppelklick auf einen TButton machst in Delphi. Dann legt Delphi eine ButtonClick Prozedur an und weißt die auch gleich dem Event zu. Das machst du hier auch, nur händig. // Hauptprogramm // viel Code vorher // OnTerminate Event für Threads // Die Parameter weiß ich nicht mehr) procedure ThreadTerminate(Sender: TObject;) begin // Das tun was getan werden soll, wenn der Thread beendet wird end; (* viel Code vorher *) (* Neuen Thread erzeugen und in Array merken*) AktivThreads[idxAktivThread] := TDownload.Create( (* Erster Parameter : Speicherort *) linkSaveName, (* Zweiter Parameter : Quelle *) link, (* Dritter Parameter : Zusätzlich Identifikations# da ThreadID doppelt vorkommt *) threadUID, (* Vierter Parameter : Create Suspended *) True ); // Terminate-Event zuweisen AktivThreads[idxAktivThreads].onTerminate:= ThreadTerminate; (* viel Code nachher *) Somit wird deine Prozedur aufgerufen, sobald der Thread sich beendet. Was das Problem bei deinem Code sein kann: procedure TDownload.Execute; begin try UrlDownloadToFile(nil, PChar(SourceFile), PChar(Speicherort), 0,nil); finally (* keine Aktion nötig, soll nur nicht abstürzen bei Fehler *) end; Synchronize(AusListeEntfernen); end; Wenn UrlDownload einen Fehler Produziert, wird AusListeEntfernen nicht aufgerufen! Das solltest du in finally schreiben, dass das also auf jeden Fall aufgerufen wird. Und um einen Fehler abzufangen musst du eh Try Except benutzen Zitieren
Maulwurf_der_Schlaue Geschrieben 7. März 2008 Autor Geschrieben 7. März 2008 Hmmm, threads in Delphi sind schon eine Weile her... ich versuch es mal. Erstmal, was mit gleich aufgefallen ist: ... * code aus Platzgründen entfernt * ... var im protected? Willst du deine Threads weitervererben? Oder die Variablen irgendwem sonst zugänglich machen? Denke doch nicht. Also solltest du die ganz normal als Felder im Private des threads deklarieren: Ok, das habe ich wie vorgeschlagen korrigiert. Hierzu habe ich keine Einwände weil es einfach so stimmt ... * code aus Platzgründen entfernt * ... Und ohne var davor. Weiter die Einbindung von Form1. :eek Ich hab jetzt keine Delphi-Hilfe mehr da oder sonst was, aber sowas ist ein abolutes NoGo. Zum einen beziehst du dich direkt auf den Formnamen (wobei form1 auch nicht form1 heißen sollte, aber egal ). Wenn du den mal änderst? Oder wenn du die Threads in einem anderen Programm verwenden willst? Wenn du ein zweites Formulas hinzufügst, was dann den Threadaufruf startet? Der Sinn der modularen Programmierung ist ja, dass die Module (die einzelnen Units) recht frei verwendet werden können, auch in anderen Programmen. Genau das verhinderst du ja hier, weil du alles von deinem Hauptformular abhängig machst. Soweit habe ich mir noch keine Gedanken gemacht ob ich die Unit wirklich in anderen Projekten verwenden möchte. Ich arbeitet zurzeit an der fünften Version des Programms. Die Threads sind in dieser Version neu weil ich so mehrere Aufgaben gleichzeitig verarbeiten kann ohne auf "Wartezeiten" andere Faktoren Rücksicht nehmen zu müssen. Also: Die Prozedur AusListeEntfernen entfernen. Das uses Code entfernen. In der Objektorientierung gibt es etwas wunderbares, nämlich die Events. Ein TThread bietet auch ein paar Events, unter anderem das OnTerminate-Event. Das wird genau dann abgefeuert, wenn der Thread sich beendet. Das solltest du dann auch nutzen. Ich kenn die genaue Signatur des Events nicht mehr, aber die Hilfe sagt dir da ne Menge. (Hab ich schonmal gesagt, dass ich die Delphi Hilfe liebe? Ich hab noch keine Hilfe gesehen, die so ausführlich und gut ist wie die von Delphi. Weder Visual Studio, MSDN, noch Java oder ähnliches haben auch nur annähernd so eine gute Hilfe wie Delphi!) Falls du das noch nie gemacht hast hier eine kleine Erklärung: Einem Event kannst du eine Prozedur zuweisen, die dann ausgeführt wird, wenn das Event aufgerufen wird. Die Prozedur kann irgendeine sein, sie muss nur dieselben Parameter haben wie die geforderten. du kennst es vielleicht, wenn du einen Doppelklick auf einen TButton machst in Delphi. Dann legt Delphi eine ButtonClick Prozedur an und weißt die auch gleich dem Event zu. Das machst du hier auch, nur händig. ... * code aus Platzgründen entfernt * ... Somit wird deine Prozedur aufgerufen, sobald der Thread sich beendet. Wenn ich das mit OnTerminate erledige, was durchaus auch in Ordnung wäre habe ich keine Möglichkeit mehr im Hauptformular das Ereignis zu visualisieren. Bisher konnte ich genau herausfinden welcher Thread fertig war und welcher noch nicht. Hierzu ist eben der Griff über "Form1" nötig. Signatur : OnTerminate(Sender: TObject) Was das Problem bei deinem Code sein kann: ... * code aus Platzgründen entfernt * ... Wenn UrlDownload einen Fehler Produziert, wird AusListeEntfernen nicht aufgerufen! Das solltest du in finally schreiben, dass das also auf jeden Fall aufgerufen wird. Und um einen Fehler abzufangen musst du eh Try Except benutzen Ich dachte immer das alles nach end des finally auch durchlaufen wird. Werde diese Fehlerquelle korrigieren. Werde nochmal einen Langzeittest durchführen um zu sehen ob das Programm immer noch Threads verliert oder ob es sich gebessert hat. Danke für bereits geleistet Unterstützung. Zitieren
geloescht_JesterDay Geschrieben 8. März 2008 Geschrieben 8. März 2008 Wenn ich das mit OnTerminate erledige, was durchaus auch in Ordnung wäre habe ich keine Möglichkeit mehr im Hauptformular das Ereignis zu visualisieren. Bisher konnte ich genau herausfinden welcher Thread fertig war und welcher noch nicht. Hierzu ist eben der Griff über "Form1" nötig. Natürlich kannst du das im Hauptformular visualisieren und auch sonst alles tun was du willst. OnTerminate nimmt eine prozedur auf, und die kann kommen von wo sie will. Die ThreadTerminate-Prozedur in meinem Beispiel sollte eben im Form1 implementiert sein, das hab ich nur nicht dazugeschrieben, da ich dachte das wäre klar Solch eine Verschränkung wie bei dir ist unabhängig vom Modularen schon schlecht. Das ist einfach ein schlechter Stil. Mehr kann ich dazu im Moment gar nicht sagen, es ist einfach... schlecht das allein zu sehen Solche Events sind genau dafür da, dass du von außen auf irgendwas in Objekten reagieren kannst. Ohne dass das Außen irgendwas mit dem Objekt zu tun haben muss. Im Prinzip macht das ja nichts anderes als das was du getan hast. Ich weiß nicht ob du schonmal selber ein Event programmiert hast, sieht aber nicht so aus. Also: Ein Event besteht erstmal aus einem Prozedurzeiger, der ist erstmal leer. Wenn du dem Event eine Prozedur zuweißt, dann steht diese da im Zeiger. Kommt der Code jetzt an diese Stelle wo ein Event ausgelöst werden soll, dann geschieht das, indem geprüft wird, ob der Zeiger leer ist, und wenn nicht, wird die Prozedur auf die der Zeiger verweißt aufgerufen. Also ganz einfach ung so: //*** Hier steht u.U. bissl Code type TThreadTerminateEvent = procedure(Sender: TObject); //*** Hier ist die Klasse definiert TThread = Class(Thread) // Keine Ahnung wie die genau aussieht ... private //*** hier der Prozedurzeiger FThreadTerminate: TThreadTerminate; ... public //*** hier sind die Events als public definiert OnTerminate: TThreadTerminate; read FThreadTerminate; write FThreadTerminate; // Denke das war so... kannst dir ja aber den Quelltext anschauen // wenn du willst ... end; //*** eine Menge mehr Code //*** jetzt passiert was und ich als Entwickler des Objekts will, //*** dass man das von außen mitbekommen kann //*** in dem Beispiel ist der Thread beendet if (assigned(FThreadTerminate)) then begin FThreadTerminate(self); end; ... Du kannst also einfach den Code deiner Prozedur in deine ThreatTerminate Prozedur kopieren und alles geht gehauso wie vorher... den Thread bekommst du über den Sender, musst ihn u.U. halt casten vor dem benutzen z.B. (Sender as TThread).ThreadID Und wenn du nicht sicher bist, dass das immer ein TThread ist noch eine Prüfung If Sender is TThread davor. EDIT: Natürlich könntest du auch das ganze überschreiben und dein eigenes Event als OnTerminate ausführen... da könntest du dann wohl deine ThreadID oder was auch immer als Parameter an die Prozedur übergeben. Also ein Event ist im Grunde nichts anderes als das was du gemacht hast, nur eben Source-unabhängig, weil du die aufgerufene Prozedur erst zur Laufzeit da reinkopierst. Zitieren
geloescht_JesterDay Geschrieben 8. März 2008 Geschrieben 8. März 2008 Noch was: Ich dachte immer das alles nach end des finally auch durchlaufen wird. Nein, gerade das wird ja nicht durchlaufen. Ansonsten würde das ja keinen Sinn machen. Wenn ein Fehler auftritt, wird die Ausführung normal an der Stelle abgebrochen. Alles was danach steht wird nie aufgerufen. Aus dem Grund gibt es try finally. Im Try block ist der Code, der Probleme bereiten könnte und im Finally Block ist das, was auch wenn ein Fehler auftritt, auf jeden Fall vor dem Rücksprung noch ausgeführt werden soll. Und nur der Finally Block wird dann ausgeführt vor dem Abbruch. EDIT: Wenn du das mit einem Try Except machst so wie du das mit dem Finally hast bei dir, dann würde der Code ausgeführt werden. Dabei würdest du aber den Fehler einfach verschlucken und nicht auf ihn reagieren und nichts. Zitieren
Maulwurf_der_Schlaue Geschrieben 8. März 2008 Autor Geschrieben 8. März 2008 Hallo JesterDay, erstmal danke für eine ausführlichen Antworten. Natürlich kannst du das im Hauptformular visualisieren und auch sonst alles tun was du willst. Das dachte ich mir schon fast das das geht, nur weiß _ich_ nicht wie das geht. Hätte mich vllt. anders ausdrücken sollen. Zitieren
Maulwurf_der_Schlaue Geschrieben 18. März 2008 Autor Geschrieben 18. März 2008 Hallo zusammen, ich konnte mich leider nicht eher melden, dafür hab ich aber was mitgebracht :cool: Ich denke das Problem mit den verschollenen Threads habe ich behoben. Habe mir hierzu ein kleines Programm geschrieben, um die Thread-Thematik ohne großes "Drumherum" zu analysieren. Da ich das Programm geschrieben habe, wird auch mein "unschöner" Code verwendet. Damit ich aber auch hier etwas dazulernen kann, bitte ich euch das Ihr über mein Programm schaut und mir dann mitteilt wie man das "schöner" lösen könnte. Schon einmal vielen Dank vorab. Ich bitte um Verständnis das ich zum Thema Programmierstil in diesem Fall kein neues Thema eröffne, da es sonst aus dem Zusammenhang gerissen werden wird.MultiThread.zip Zitieren
geloescht_JesterDay Geschrieben 19. März 2008 Geschrieben 19. März 2008 Werd es mir mal ansehen, wohl über die Feiertage, wenn mir langweilig ist Bitte jetzt schon um Verständnid, dass ich es vielleicht nicht testen kann, mangels Delphi Zitieren
geloescht_JesterDay Geschrieben 20. März 2008 Geschrieben 20. März 2008 So, hatte grad mal nichts zu tun. Nach bestem Wissen und Gewissen geändert. Hab Kommentare dazu geschrieben was und warum Konnte das halt nicht testen, aber denke das könnte sogar so gehen (Naja, zu meiner aktiven Delphi Zeit hat ich das nie, dass ich Code geschrieben hab und der ging sofort. Und wenn es nur ein Tippfelhler war)MultiThreadRevised.zip Zitieren
Maulwurf_der_Schlaue Geschrieben 22. März 2008 Autor Geschrieben 22. März 2008 Hallo JesterDay, werde mir deine Verbesserungen ansehen. Danke. Zitieren
Maulwurf_der_Schlaue Geschrieben 22. März 2008 Autor Geschrieben 22. März 2008 Guten Abend, habe mir jetzt den ganzen Nachmittag bis jetzt das Programm angesehen und habe zur Vereinfachung ein neues geschrieben mit den wesentlichen Verbesserungen. Die neue Version befindet sich im Anhang. Nachdem die Syntaxfehler behoben waren, die man automatisch macht wenn man den Code nur im "Windows Editor" schreibt bekam ich noch echte "Probleme". Ich erhielt ständig Zugriffsverletzungen im Speicher. Ich kam nicht dahinter wie/wo diese entstehen bzw. es mir unerklärlich war, habe ich kurzerhand einfach Version 2.0 erstellt. Selbstverständlich mit den entsprechenden Verbesserungen und sogar ein wenig den Stil beachtet Es kam dann zu folgenden Problemen: Das Programm ist immer abgestürzt als es den FreeAndNil-Befehl durchführte. Deswegen habe ich diesen dann entfernt , hörte sich von der Definition nach nicht schlecht an. Wobei ich sowieso nicht sicher bin ob es FreeOnTerminate := True und FreeAndNil() gemeinsam bringen. Vielleicht weiß jemand (*schiel zu JesterDay*) mehr Das nächste war von der Grundfunktion her: Bei meiner Version konnte man nachdem man 12 Threads gestartet hatte und diese fertig waren, erneut 12 Threads starten. Dies ging in der neuen Version nicht mehr da hier das Array dynamisch erweitert wurde mit SetLength(). Es war z.B. möglich wenn 12 Threads liefen und einer davon fertig war, man sofort wieder einen "nachstarten" konnte, weil ja wieder ein Platz frei wurde von den maximal 12 . Da war meine Version dann doch einfacher umzusetzen mit for.to.do -> Hab aber trotzdem Teile übernommen, jetzt muss man nur eine Konstante ändern um das Array zu vergrößern/verkleinern. (Böse Falle ist weg ) Sonst hat bei Version 2.0 alles gepasst Ich wusste z.B. das Variablen in der OnTerminate-Prozedur nicht mehr verfügbar sind, weswegen ich dies umständlich gelöst habe. Durch den modifizierten Code habe ich festgestellt das das nicht für "Properties" gilt. Somit konnte ich den Event OnTerminate des Threads verwenden. Ich hab diesmal sogar Kommentare hinterlassen Sind zwar noch nicht so gut wie die von JesterDay aber die Übung macht den Meister Vielen Dank JesterDay. :uliMultiThread2.0.zip Zitieren
geloescht_JesterDay Geschrieben 22. März 2008 Geschrieben 22. März 2008 Wobei ich sowieso nicht sicher bin ob es FreeOnTerminate := True und FreeAndNil() gemeinsam bringen. Vielleicht weiß jemand (*schiel zu JesterDay*) mehr Wenn ich das gewußt hätte, dann hät ich dazu was gesagt oder das verbessert. Sagte ja im Kommentar (glaub ich doch), dass es da zu Problemen kommen kann. Der Thread ist noch nciht fertig und wird schon freigegeben... du sägst dir den Ast ab auf dem du sitzt. Zu FreeOnTerminate... da kann die die Hilfe dazu alles sagen was du wissen musst und was genau das bewirkt. Jetzt mal ernsthaft. Die Delphi Hilfe hilft einem wirklich. Ich denke ja mal, dass die nicht schlechter geworden ist seit Version 7 (und allen davor, bis 4 kenn ich die). Da steht dann drin, was genau das tut und ob der Zeiger auch auf nil gesetzt wird o.ä. Wenn nicht, was ja ansich kein Fehler ist, darfst du halt nicht mit <> nil prüfen Aber das hat ja scheinbar funktioniert, also wird da wohl was getan... wie auch immer (siehe Ast und absägen). Also die Delphi Hilfe hilft dir bestimmt so gut wie ich. Da hab ich auch viel gelernt. Zu den anderen Problemen... naja, solche konnt ich natürlich nich direkt erkennen und am Design der Anwendung musst du schon selber basteln. Das geht nur mit nem Texteditor schwer. Mit den Threads neu starten... ja, stimmt. Das is bei mir nich so gut. Mal als idee: Du könntest dir die vergebenen Indizes in einem Set merken. Ein Set kannst du einfach mit "i in Set" prüfen ob es eine Zahl enthält. Hinzufügen und Entfernen geht glaub auch einfach, musst aber in der Hilfe nachsehen. var usedIndizes: Set of Byte; Byte = Integer nur beschränkt auf 0..255 (also praktisch dasselbe wie integer). BRaucht halt nur 1/4, eben 1 Byte. Beim beenden löschst du den index daraus, beim hinzufügen ...hmmmm... ich glaub aber eine for to do mit in geht schneller... auch wenn es auch immer länger dauert. Naja, vielleicht ist die idee für mehrfach wiederverwendbare nicht die beste... Werd mir das nochmal ansehen... wenn ich zeit hab, im Moment hab ich da keinen kopf für, vielleicht fällt mir dann auch was ein. Zitieren
Maulwurf_der_Schlaue Geschrieben 23. März 2008 Autor Geschrieben 23. März 2008 Hallo JesterDay, ich wollte Dir in keiner Weise zu nahe treten, ich bin doch froh das Du Dir das überhaupt ansiehst. Es existiert kein Zeitdruck oder ähnliches, es dient ja als Lern Beispiel wie man mit Threads umgehen kann. Ich werde versuchen mit der Delphi-Hilfe besser klar zu kommen. Genauso wenig drehe ich dir einen Strick daraus wenn da etwas im Programm nicht funktioniert hat. Das meine Eigenarbeit weiterhin erforderlich ist habe ich nicht ausgeschlossen. Ich dachte ich schreib Dir welche Probleme noch aufgetreten sind da ich deine Vorschläge verwendet habe. Also die Delphi Hilfe hilft dir bestimmt so gut wie ich. Da hab ich auch viel gelernt. Die Hilfe wird niemals einen Menschen ersetzen können , auch wenn sie noch so gut ist. Werde mir das mit der "Set of"-Thematik mal ansehen. Zitieren
geloescht_JesterDay Geschrieben 24. März 2008 Geschrieben 24. März 2008 Hallo JesterDay, ich wollte Dir in keiner Weise zu nahe treten, ich bin doch froh das Du Dir das überhaupt ansiehst. Das hab ich auch nicht so verstanden gehabt. Naja, der Mensch ist dann halt noch ne Art Suchmaschine für die Hilfe, denn er kann erkennen was du eigentlich willst. Bei der Hilfe musst du schon selber suchen Aber so allgemein zu Themen wie "Vielleicht weiß ja wer was der Unterschied zw. FreeOnTerminate und FreeAndNil ist" findest du da sehr viel. Nur musst du es u.U. halt selbt korrekt interpretieren. Dennoch kann ich nicht oft genug erwähnen, dass die Delphi Hilfe Klasse ist. MSDN kommt dann wohl mit etwas Abstand auf Rang 2, weil sie es zwar ähnlich versuchen, aber mehr als ein Versuch ist es nicht da ranzukommen. Java Hilfe ist eigentlich nur ne Klassenbeschreibung. Bei MSDN gibt es wenigstens ein paar Beispiele, die aber meist total daneben sind und mit dem was man sucht nur wenig zu tun haben. Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.