Ich glaube da ist ein Fehler drin.
Dort wird im Thread-Konstruktor FreeOnTerminate auf true gesetzt. Das bedeutet doch, dass der Thread nach der Ausführung von "Execute" sich selbstständig terminiert. Und in der Cleanup-Funktion wird "Terminate()" aufgerufen. Das ist doch überflüssig, oder?
_________________ Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.
@sonic:
Ich habe mir das Tutorial jetzt nicht angesehen, aber der Aufruf von Terminate() beendet einen Thread. Wenn FreeOnTerminate=true ist, muss man weiter nichts machen. Wenn es false ist, muss man den Thread auch noch löschen.
Der Aufruf von Terminate() ist nicht grundsätzlich verboten, auch wenn der Thread mit FreeOnTerminate=true ist. Es gibt Threads die laufen in einer kontunierlichen Schleife, diese muss man mit Terminate() beenden. Wenn ein Thread allerdings eine eigene Abbruchbedingung hat, sollte man Terminate() nicht verwenden.
@audacia
Der bleibt in der Zeile 19, also dem WaitResult := ... stehen. Zumindest ist das die aufrufende Funktion. Viel häufiger wird mir allerdings der Aufruf von Sleep als aufrufende Funktion angezeigt (in 9 von 10 Fällen).
Das verstehe ich dann jetzt überhaupt nicht. Ich mein, es ist nur ein 200 ms Sleep, wie kann das mehrere Sekunden beanspruchen?!? Könnte das an meiner Hardware liegen? Der Rechner ist zugegebenermaßen schon ziemlich alt.
@audacia
Der bleibt in der Zeile 19, also dem WaitResult := ... stehen. Zumindest ist das die aufrufende Funktion. Viel häufiger wird mir allerdings der Aufruf von Sleep als aufrufende Funktion angezeigt (in 9 von 10 Fällen).
Das verstehe ich dann jetzt überhaupt nicht. Ich mein, es ist nur ein 200 ms Sleep, wie kann das mehrere Sekunden beanspruchen?!? Könnte das an meiner Hardware liegen? Der Rechner ist zugegebenermaßen schon ziemlich alt.
Wir hatten bei einem Projekt genau das gleiche Verhalten, nur dass es nicht 7 Sekunden, sondern 30+ waren. Das Szenario war das gleiche, im Destruktor des TThreads blieb die CPU auf WaitFor stehen. Das Verhalten war nicht immer 100%ig (eigentlich war das korrekte Verhalten nicht reproduzierbar, in 4 von 5 Fällen hing der Thread im Destruktor) reproduzierbar, es trat sowohl auf zwei Entwicklungsrechnern als auch auf einem Produktionssystem auf.
Das Problem mit dem blockieren im WaitFor des Threads liegt darin, dass WaitFor intern einen Sleep(0) verwendet. Dieser gibt den Prozessor nur an Threads mit gleicher Priorität ab.
Wenn auf einen Thread mit tieferer Priorität gewartet wird, erhält dieser keine CPU-Zeit (ausser auf Mehr-Prozessor-Systemen).
Zur Abhilfe sollte die Priorität des Threads, auf den gewartet wird vor dem WaitFor (oder vor Terminate) auf mindestens die Priorität des aktuellen Threads angehoben werden.
Ich bin sprachlos. Mit einem Priority = tpHigher vor der Terminate()-Aufruf, ist das Problem tatsächlich weg. Was hab ich damals nicht alles probiert, aber darauf bin ich nicht gekommen...
Aber im Beispiel zu Priority wird die Priorität gesetzt, bevor der Thread mit Resume fortgesetzt ist. Ist das nur Zufall, oder soll das ein Hinweis darauf sein, dass man die Priorität zur Laufzeit nicht ändern sollte?
Ich habe schon öfters Thread-Prioritäten zur Laufzeit geändert. Konnte bisher nie irgendein Nachteil feststellen. In der Doku habe ich auch keine Hinweise gefunden, die dies als schlecht einstufen.
Das Problem mit dem blockieren im WaitFor des Threads liegt darin, dass WaitFor intern einen Sleep(0) verwendet. Dieser gibt den Prozessor nur an Threads mit gleicher Priorität ab.
Wenn auf einen Thread mit tieferer Priorität gewartet wird, erhält dieser keine CPU-Zeit (ausser auf Mehr-Prozessor-Systemen).
Tatsächlich - ist ja auch so in der Dokumentation zu Sleep() nachlesbar. *facepalm*
Zu dem Problem gibt es auch schon einen QC-Report: #3260. Allerdings sollte das Problem mittlerweile nicht mehr auftreten, denn in C++Builder 2006 und höher verwendet TThread.WaitFor kein Sleep(0) mehr:
function TThread.WaitFor: LongWord;
{$IFDEF MSWINDOWS}
var
H: array[0..1] of THandle;
WaitResult: Cardinal;
Msg: TMsg;
begin
H[0] := FHandle;
if GetCurrentThreadID = MainThreadID then
begin
WaitResult := 0;
H[1] := SyncEvent;
repeat
{ This prevents a potential deadlock if the background thread
does a SendMessage to the foreground thread }
if WaitResult = WAIT_OBJECT_0 + 2 then
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
CheckThreadError(WaitResult <> WAIT_FAILED);
if WaitResult = WAIT_OBJECT_0 + 1 then
CheckSynchronize;
until WaitResult = WAIT_OBJECT_0;
end else WaitForSingleObject(H[0], INFINITE);
CheckThreadError(GetExitCodeThread(H[0], Result));
end;
{$ENDIF}
function TThread.WaitFor: LongWord;
{$IFDEF MSWINDOWS}
var
H: array[0..1] of THandle;
WaitResult: Cardinal;
Msg: TMsg;
begin
H[0] := FHandle;
if GetCurrentThreadID = MainThreadID then
begin
WaitResult := 0;
H[1] := SyncEvent;
repeat
{ This prevents a potential deadlock if the background thread
does a SendMessage to the foreground thread }
if WaitResult = WAIT_OBJECT_0 + 2 then
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
CheckThreadError(WaitResult <> WAIT_FAILED);
if WaitResult = WAIT_OBJECT_0 + 1 then
CheckSynchronize;
until WaitResult = WAIT_OBJECT_0;
end else WaitForSingleObject(H[0], INFINITE);
CheckThreadError(GetExitCodeThread(H[0], Result));
end;
{$ENDIF}
function TThread.WaitFor: LongWord;
{$IFDEF MSWINDOWS}
var
H: array[0..1] of THandle;
WaitResult: Cardinal;
Msg: TMsg;
begin
H[0] := FHandle;
if GetCurrentThreadID = MainThreadID then
begin
WaitResult := 0;
H[1] := SyncEvent;
repeat
{ This prevents a potential deadlock if the background thread
does a SendMessage to the foreground thread }
if WaitResult = WAIT_OBJECT_0 + 2 then
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
CheckThreadError(WaitResult <> WAIT_FAILED);
if WaitResult = WAIT_OBJECT_0 + 1 then
CheckSynchronize;
until WaitResult = WAIT_OBJECT_0;
end else WaitForSingleObject(H[0], INFINITE);
CheckThreadError(GetExitCodeThread(H[0], Result));
end;
{$ENDIF}
Das erklärt zwar nicht, weshalb ich das Problem mit C++Builder 6 nicht reproduzieren kann (SetProcessAffinityMask() habe ich natürlich getestet), aber in neueren Versionen sollte es nicht mehr auftreten.
Das erklärt zwar nicht, weshalb ich das Problem mit C++Builder 6 nicht reproduzieren kann (SetProcessAffinityMask() habe ich natürlich getestet), aber in neueren Versionen sollte es nicht mehr auftreten.
Zwei, sonst wäre SetProcessAffinityMask() redundant
Das wird sicherlich auch die Ursache sein; allerdings ist mir noch nicht ganz klar, weshalb sich das Problem nicht mit SetProcessAffinityMask() reproduzieren läßt. (Vielleicht arbeitet der Thread-Scheduler auf Mehrkernsystemen grundsätzlich anders? Mal in Windows Internals nachlesen.)
ich habe noch eine Frage zum Thema Thread-Erstellung.
Ich habe einen Thread, in dessen Execute-Funktion ein Button aufgerufen wird.
Die Funktion in diesem Button schreibt Daten in eine TStringGrid rein. In der OnDrawCell von dieser StringGrid
färbe ich die Zellen abhängig von den Zelleninhalten.
Jetzt kriege ich sporadisch Fehlermeldungen "Zugriffsverletzung an der Adresse xxx" oder "Falsche Zeigeroperation". Der Fehler wird durch
den Zugriff auf den Zelleninhalt ausgelöst. Das habe ich mit dem Debugger geprüft.
Kann es sein, dass die OnDrawCell() mit meinem Thread irgendwie synchronisiert werden muss? Weil sie ja im Hauptthread
aufgerufen wird. Oder die StringGrid ist nicht Thread-sicher
_________________ Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.
Zuletzt bearbeitet von sonic_1233 am 09:42:46 11.02.2010, insgesamt 1-mal bearbeitet
Nächstes Thema anzeigen Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums
für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de
Werbekostenerstattung verdient werden kann.
Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de
enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt
(vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in
privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden.
Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere
übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.