Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   
Forentreff 2012     
Bücher-Shop mit Amazon (Buchkategorien)C++ : Referenzen zu C++ : C++ Builder : Visual C++ : C# : Java : Spieleprogrammierung : Systemprogrammierung Linux : Software-Entwicklung : .NET : Compilertechnik : Algorithmen & Datenstrukturen : Objektorientierung : Entwurfsmuster : UML : eXtreme Programming : Scrum : Projektmanagement : Software-Testing : Datenbanken : Tom DeMarco : Dilbert : User Friendly
C/C++ Forum :: VCL (C++ Builder) ::  Wie erstellt man einen neuen Thread?     Zeige alle Beiträge auf einer Seite Auf Beitrag antworten
Autor Nachricht
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 15:03:52 04.02.2010   Titel:   Wie erstellt man einen neuen Thread?            Zitieren

Hallo Leute,

ich möchte in einer Anwendung, die ich mit in BCB 5.0 Pro erstellt habe, einen Thread erstellen.
Gibt es hier, im Forum, eine Anleitung dazu? Ich finde nichts dazu

_________________
Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 15:11:42 04.02.2010   Titel:              Zitieren

Datei, Neu, Thread-Objekt.

Details stehen in der Hilfe; wenn du noch nicht vertraut mit Multithreading bist, lies ein gutes Tutorial zum Thema, etwa dieses hier.

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 15:23:56 04.02.2010   Titel:              Zitieren

Vielen Dank

_________________
Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.
DocShoe
Mitglied

Benutzerprofil
Anmeldungsdatum: 02.04.2008
Beiträge: 1605
Beitrag DocShoe Mitglied 16:16:25 04.02.2010   Titel:              Zitieren

Wenn das ein reiner Worker Thread ohne Interaktion mit der Benutzeroberfläche sein soll würde ich lieber Win32 Threads benutzen. Die aus der VCL haben so komische Randbedingungen, die erfüllt sein müssen (z.B. beim Stoppen). Die dazu notwendigen Funktionen heissen CreateThread, SuspendThread, ResumeThread und TerminateThread.
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 17:25:27 04.02.2010   Titel:              Zitieren

Also ich habe jetzt was zusammengebaut. In einer Timerfunktion erstelle ich alle 100 ms einen neuen Thread. Es läuft auch eine Zeit lang gut. Nach etwa 2 Minuten wird das Programm immer langsamer, und dann kommt die Fehlermeldung "Abnormal termination".
Ich denke, dass die erstellten Threads nicht richtig beendet werden.

Weiss vielleicht jemand von euch, was ich falsch gemacht habe?

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Frame_Auswertung *Frameauswertung;

void __fastcall TForm1::TimerEmpfangene_Daten_auswertenTimer(
      TObject *Sender)
{

 // Neuen Thread für die Auswertung starten
 // =======================================

 Frameauswertung = new Frame_Auswertung(false);

 // Funktion zuweisen:
 Frameauswertung->OnTerminate = DeleteThread;

 Frameauswertung->FreeOnTerminate = true;
}

void __fastcall TForm1::DeleteThread(TObject *Sender)
{
 Frameauswertung = NULL;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Frame_Auswertung *Frameauswertung;

void __fastcall TForm1::TimerEmpfangene_Daten_auswertenTimer(
TObject *Sender)
{

// Neuen Thread für die Auswertung starten
// =======================================

Frameauswertung = new Frame_Auswertung(false);

// Funktion zuweisen:
Frameauswertung->OnTerminate = DeleteThread;

Frameauswertung->FreeOnTerminate = true;
}

void __fastcall TForm1::DeleteThread(TObject *Sender)
{
Frameauswertung = NULL;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Frame_Auswertung *Frameauswertung;

void __fastcall TForm1::TimerEmpfangene_Daten_auswertenTimer(
      TObject *Sender)
{

 // Neuen Thread für die Auswertung starten
 // =======================================

 Frameauswertung = new Frame_Auswertung(false);

 // Funktion zuweisen:
 Frameauswertung->OnTerminate = DeleteThread;

 Frameauswertung->FreeOnTerminate = true;
}

void __fastcall TForm1::DeleteThread(TObject *Sender)
{
 Frameauswertung = NULL;
}

_________________
Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 21:51:45 04.02.2010   Titel:              Zitieren

DocShoe schrieb:
Die aus der VCL haben so komische Randbedingungen, die erfüllt sein müssen (z.B. beim Stoppen).
Was meinst du?
Für einen Worker-Thread ist das okay, aber ansonsten ist der Verzicht auf Synchronize() und Queue() IMO unangenehm.


DocShoe schrieb:
Die dazu notwendigen Funktionen heissen CreateThread, SuspendThread, ResumeThread und TerminateThread.
Da muß ich gleich dreimal entschieden widersprechen.

  • CreateThread sollte in C- und C++-Programmen nicht verwendet werden, da die C-RTL threadweise initialisiert werden muß. Darauf weist unter anderem die Dokumentation im MSDN hin:
    MSDN schrieb:
    A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multi-threaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.
    Das ist in der C++Builder-RTL nicht anders.

  • Der einzige legitime Grund zur Verwendung von ResumeThread ist das Starten eines mit CREATE_SUSPENDED erstellten Threads. Ansonsten ist die Verwendung von SuspendThread und ResumeThread dringend zu vermeiden, denn das kann zu völlig unvorhersehbaren Deadlocks führen, wenn der Thread gerade ein Lock auf irgendeine Ressource hält. Auch das ist im MSDN nachzulesen:
    MSDN schrieb:
    This function is primarily designed for use by debuggers. It is not intended to be used for thread synchronization. Calling SuspendThread on a thread that owns a synchronization object, such as a mutex or critical section, can lead to a deadlock if the calling thread tries to obtain a synchronization object owned by a suspended thread. To avoid this situation, a thread within an application that is not a debugger should signal the other thread to suspend itself. The target thread must be designed to watch for this signal and respond appropriately.


  • Schließlich ist TerminateThread mit Sicherheit von allen Lösungen, einen Thread zu beenden, die schlechteste. Du findest dazu auch etwa auf StackOverflow Hinweise, aber auch hier ist MSDN sehr explizit:
    MSDN schrieb:
    TerminateThread is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code. DLLs attached to the thread are not notified that the thread is terminating. The system frees the thread's initial stack.

    Windows Server 2003 and Windows XP/2000: The target thread's initial stack is not freed, causing a resource leak.

    TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:

    • If the target thread owns a critical section, the critical section will not be released.
    • If the target thread is allocating memory from the heap, the heap lock will not be released.
    • If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
    • If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.


Die richtigen Alternativen sind _beginthread()/_beginthreadex(), die Event-Funktionen im Windows-API sowie ein derartiges Thread-Design, daß der gewünschte Abbruch durch ein Event signalisiert werden kann. Oder, wenn du es einfach haben willst, einfach ein Wrapper wie TThread.

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 22:06:08 04.02.2010   Titel:              Zitieren

sonic_1233 schrieb:
In einer Timerfunktion erstelle ich alle 100 ms einen neuen Thread.[...]Weiss vielleicht jemand von euch, was ich falsch gemacht habe?
Du hast das Tutorial nicht durchgelesen.


Wie im MSDN einzusehen ist, wird für jeden erstellten Thread etwa 1 MB für den Stack bereitgestellt. Das macht bei dir etwa 10 MB pro Sekunde. Jetzt noch einkalkuliert, daß eine gewöhnliche 32-Bit-Anwendung maximal knapp 2 GB virtuellen Arbeitsspeicher zugewiesen bekommen kann, und du kannst ausrechnen, daß deine Anwendung nach spätestens 3:41 Minuten keinen Adreßraum mehr zur Verfügung hat. In der Realität tritt das natürlich früher ein, weil der Adreßraum ja nicht nur für deine Threads da ist.


MSDN schrieb:
The number of threads a process can create is limited by the available virtual memory. By default, every thread has one megabyte of stack space. Therefore, you can create at most 2,048 threads. If you reduce the default stack size, you can create more threads. However, your application will have better performance if you create one thread per processor and build queues of requests for which the application maintains the context information. A thread would process all requests in a queue before processing requests in the next queue.


Anders gesagt, erstelle einen einzigen Thread, der auf Anweisungen wartet (WaitForSingleObject), sie abarbeitet, dem UI-Thread mittels TThrea::Synchronize() das Ergebnis übergibt und dann von vorne anfängt.

Es ist nicht sinnvoll, mit Threads einfach so draufloszuprogrammieren, ohne sich zuvor informiert zu haben. Multithreading gehört mit zu den komplexesten Systemabläufen, mit denen sich ein Anwendungsprogrammierer auseinandersetzen muß; wenn du dich nicht gut vorbereitest, wirst du nie in der Lage sein, die unvermeidlichen multithreading-verursachten Abstürze in deiner Anwendung zu beheben. Also reiß dich zusammen und arbeite das Tutorial durch, bevor du fortfährst.

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
Killer-Kobold
Mitglied

Benutzerprofil
Anmeldungsdatum: 16.03.2009
Beiträge: 180
Beitrag Killer-Kobold Mitglied 23:42:10 04.02.2010   Titel:              Zitieren

audacia schrieb:
Also reiß dich zusammen und arbeite das Tutorial durch, bevor du fortfährst.

Ach was, wann hat man sonst die Möglichkeit, so schöne Fehlermeldungen, wie 'Leinwand erlaubt kein zeichnen' zu erhalten. ;)

Ich hab mit den VCL-Threads aber auch so meine Probleme. Ich hab keine Möglichkeit gefunden, laufende Threads, mit VCL-Mitteln, sinnvoll zu beenden, wenn der User die Anwendung beendet. Da muß man tatsächlich auf Events und WaitForXXX zurückgreifen. Oder ich hab mich wieder zu blöd angestellt...

Gruß KK
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 09:57:42 05.02.2010   Titel:              Zitieren

Ich habe das Thema wohl auf die leichte Schulter genommen. Aber nur, weil
mein Chef alles am besten gestern fertig haben will.
Danke für alle Kommentare. Ich werde gleich das Tutorial durchlesen.

_________________
Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.
DocShoe
Mitglied

Benutzerprofil
Anmeldungsdatum: 02.04.2008
Beiträge: 1605
Beitrag DocShoe Mitglied 10:55:08 05.02.2010   Titel:              Zitieren

@Audacia
Ich habe nur die 4 wichtigsten Funktionen zur Win32 Threadverwaltung genannt, vielleicht bin ich fälschlicherweise davon ausgegangen, dass der OP sich mit Thread Design und Verhinderung von race conditions auskennt (die man im übrigen auch mit TThread haben kann). Ich stimme dir insofern zu, dass man die Techniken zur Threadsynchronisation beherrschen muss, um mit den Win32 API Funktionen ein robustes Thread Design zu erstellen.

Das Verhalten eines von TThread abgeleiteten Threads kann mitunter schon seltsam sein, insbesondere wenn ein Thread gestoppt werden soll, der noch nie gelaufen ist. Im Destruktor ~TThread() wird allerhand Synchroniasationszeugs getrieben, wobei es bei uns schon bis zu 30 Sekunden gedauert hat, bis die CPU aus dem Destruktoraufruf zurückgekommen ist. Leider konnten wir die genauen Randbedingungen nie vollständig ermitteln, aber ein Wechsel von TThread auf Threads durch die Win32 API hat das Problem instantan gelöst.
Wie du schon erwähntest steht man dann aber vor dem Problem, dass man aus den Win32 Threads keine GUI Aktualisierung anstossen kann (jedenfalls nicht durch direkte Methodenaufrufe irgendwelcher VCL Objekte), da die VCL kein Multithreading beherrscht und von sich aus auch keine Hilfsmitteln anbietet, um sich mit nicht VCL-Threads zu synchronisieren. Ich denke aber, dass man eine Synchronisation mit benutzerdefinierten Nachrichten und PostMessage hinbekommt.
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 14:27:36 05.02.2010   Titel:              Zitieren

sonic_1233 schrieb:
Aber nur, weil mein Chef alles am besten gestern fertig haben will.
Das ist eigentlich immer der Fall, und nie eine gute Ausrede für laxes Vorgehen ;)


DocShoe schrieb:
Ich habe nur die 4 wichtigsten Funktionen zur Win32 Threadverwaltung genannt
Nein, leider nicht. Du hast vier Funktionen genannt, die man allesamt, mit der Ausnahme von ResumeThread speziell für CREATE_SUSPENDED, tunlichst meiden sollte ;)

Die wichtigsten Funktionen zur Win32-Threadverwaltung heißen _beginthread[ex](), _endthread[ex](), WaitForSingleObject und Create/SetEvent/ResetEvent.


DocShoe schrieb:
Im Destruktor ~TThread() wird allerhand Synchroniasationszeugs getrieben, wobei es bei uns schon bis zu 30 Sekunden gedauert hat, bis die CPU aus dem Destruktoraufruf zurückgekommen ist. Leider konnten wir die genauen Randbedingungen nie vollständig ermitteln
TThread.Destroy sieht so aus:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
destructor TThread.Destroy;
begin
  if (FThreadID <> 0) and not FFinished and not FExternalThread then
  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;
  RemoveQueuedEvents(Self);
{$IFDEF MSWINDOWS}
  if (FHandle <> 0) and not FExternalThread then CloseHandle(FHandle);
{$ENDIF}
{$IFDEF LINUX}
  // This final check is to ensure that even if the thread was never waited on
  // its resources will be freed.
  if (FThreadID <> 0) and not FExternalThread then pthread_detach(FThreadID);
  sem_destroy(FCreateSuspendedSem);
{$ENDIF}
  inherited Destroy;
  FFatalException.Free;
end;
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
destructor TThread.Destroy;
begin
if (FThreadID <> 0) and not FFinished and not FExternalThread then
begin
Terminate;
if FCreateSuspended then
Resume;
WaitFor;
end;
RemoveQueuedEvents(Self);
{$IFDEF MSWINDOWS}
if (FHandle <> 0) and not FExternalThread then CloseHandle(FHandle);
{$ENDIF}
{$IFDEF LINUX}
// This final check is to ensure that even if the thread was never waited on
// its resources will be freed.
if (FThreadID <> 0) and not FExternalThread then pthread_detach(FThreadID);
sem_destroy(FCreateSuspendedSem);
{$ENDIF}
inherited Destroy;
FFatalException.Free;
end;
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
destructor TThread.Destroy;
begin
  if (FThreadID <> 0) and not FFinished and not FExternalThread then
  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;
  RemoveQueuedEvents(Self);
{$IFDEF MSWINDOWS}
  if (FHandle <> 0) and not FExternalThread then CloseHandle(FHandle);
{$ENDIF}
{$IFDEF LINUX}
  // This final check is to ensure that even if the thread was never waited on
  // its resources will be freed.
  if (FThreadID <> 0) and not FExternalThread then pthread_detach(FThreadID);
  sem_destroy(FCreateSuspendedSem);
{$ENDIF}
  inherited Destroy;
  FFatalException.Free;
end;
Die einzige Stelle, an der realistisch gesehen 30s-Verzögerungen entstehen können ist "WaitFor;" - und das hängt natürlich davon ab, was in deiner Execute()-Methode passiert. Wenn du da nicht gelegentlich überprüfst, ob das Terminate-Flag gesetzt ist, kanns natürlich lange dauern.


DocShoe schrieb:
Wie du schon erwähntest steht man dann aber vor dem Problem, dass man aus den Win32 Threads keine GUI Aktualisierung anstossen kann (jedenfalls nicht durch direkte Methodenaufrufe irgendwelcher VCL Objekte), da die VCL kein Multithreading beherrscht und von sich aus auch keine Hilfsmitteln anbietet, um sich mit nicht VCL-Threads zu synchronisieren. Ich denke aber, dass man eine Synchronisation mit benutzerdefinierten Nachrichten und PostMessage hinbekommt.
Klar, irgendwie möglich ist es schon - warum einfach, wenns auch umständlich geht ;)

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
Unregistrierter





Beitrag Unregistrierter 14:54:09 05.02.2010   Titel:              Zitieren

audacia schrieb:
Die einzige Stelle, an der realistisch gesehen 30s-Verzögerungen entstehen können ist "WaitFor;" - und das hängt natürlich davon ab, was in deiner Execute()-Methode passiert. Wenn du da nicht gelegentlich überprüfst, ob das Terminate-Flag gesetzt ist, kanns natürlich lange dauern.

Ich hatte das gleiche Problem: Klick mich
Wenn Du Zeit und Muße hast da mal reinzuschauen... Vielleicht kannst Du mir ja sagen, was ich da falsch gemacht habe.
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 17:46:44 05.02.2010   Titel:              Zitieren

Joe_M. schrieb:
Wenn Du Zeit und Muße hast da mal reinzuschauen... Vielleicht kannst Du mir ja sagen, was ich da falsch gemacht habe.
Zur Situation mit FreeOnTerminate=true kann ich sagen, daß dein Ansatz dann nicht ganz sicher ist:
C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
__fastcall TForm1::~TForm1(void)
{
    FMyThread->Terminate(); // -> der Thread kann ab sofort jederzeit terminiert
                            // werden

    FMyThread->WaitFor(); // ... und was, wenn das hier schon passiert ist? Dann
                          // hat sich das Objekt bereits selbst gelöscht - fatal.

    delete FMyThread; // Noch viel schlimmer - wir zerstören es zweimal.
    FMyThread = NULL;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
__fastcall TForm1::~TForm1(void)
{
FMyThread->Terminate(); // -> der Thread kann ab sofort jederzeit terminiert
// werden

FMyThread->WaitFor(); // ... und was, wenn das hier schon passiert ist? Dann
// hat sich das Objekt bereits selbst gelöscht - fatal.

delete FMyThread; // Noch viel schlimmer - wir zerstören es zweimal.
FMyThread = NULL;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
__fastcall TForm1::~TForm1(void)
{
    FMyThread->Terminate(); // -> der Thread kann ab sofort jederzeit terminiert
                            // werden

    FMyThread->WaitFor(); // ... und was, wenn das hier schon passiert ist? Dann
                          // hat sich das Objekt bereits selbst gelöscht - fatal.

    delete FMyThread; // Noch viel schlimmer - wir zerstören es zweimal.
    FMyThread = NULL;
}
FreeOnTerminate und Terminate()/WaitFor() schließen einander praktisch aus. Mehr noch: wenn ein Thread FreeOnTerminate=true verwendet, darfst du, sobald er läuft, eigentlich nicht mehr von außen auf das Thread-Objekt zugreifen - du weißt nie, ob es überhaupt noch existiert. In diesem Fall ist es notwendig, sich auf andere Kommunikationsmechanismen zu beschränken.

Selbst wenn du weißt, was der Thread macht und ausschließen kannst, daß er terminiert, bevor du Terminate() von außen aufgerufen hast, so darfst du nach dem Aufruf von Terminate() das Objekt nicht mehr benutzen, was auch ein WaitFor() ausschließt (derartige Versuche werden auch üblicherweise mit einer Exception quittiert). Infolgedessen kann dein Hauptthread nicht warten, bis der Hintergrundthread ordentlich aufgeräumt hat - und da die Beendigung des Hauptthreads auch den Prozeß beendet, wird der Hintergrundthread infolgedessen von Windows terminiert.


Außerdem das hier, was zwar nichts mit dem Threading-Problem zu tun hat, aber mir doch bemerkenswert erscheint:
C/C++ Code:
__fastcall TMonitorDirThread::~TMonitorDirThread()
{
    ...
    TThread::inherited ();
}
C/C++ Code:
__fastcall TMonitorDirThread::~TMonitorDirThread()
{
...
TThread::inherited ();
}
C/C++ Code:
__fastcall TMonitorDirThread::~TMonitorDirThread()
{
    ...
    TThread::inherited ();
}
Deaktiviere mal Laufzeitpackages, nimm dir den Debugger, setze da einen Breakpoint und schau, was da passiert.

Und um dir gleich mal den Spaß zu verderben: TThread::inherited ist ein Typedef auf TObject; du rufst also den TObject-Konstruktor auf, der ein neues, nicht initialisiertes TThread-Objekt erstellt. Ich weiß nicht, was genau du da vorhattest - vielleicht dachtest du daran, daß man beim Überschreiben virtueller Funktionen den Aufruf gelegentlich an die Basisklasse weiterleiten muß? Das ist zwar richtig, trifft jedoch auf Konstruktoren und Destruktoren in C++ nicht zu; dort passiert das automatisch. (In Delphi hingegen wäre ein "inherited Destroy;" hier durchaus richtig.)


Eine 10-15s anhaltende Verzögerung konnte ich nicht feststellen. Ich habe allerdings nur dein erstes Beispiel ausführlich getestet. Ist das mit dem zweiten besser reproduzierbar, und kannst du noch etwas ins Detail gehen hinsichtlich der notwendigen Randbedingungen (u.a. C++Builder-Version)?

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 12:10:34 08.02.2010   Titel:              Zitieren

Wie kann man eigentlich auf die Eigenschaft "Terminated" zugreifen?
Innerhalb von Execute() geht das ohne Probleme

C/C++ Code:
if(!Terminated)
{
  // tu irgendwas
}
C/C++ Code:
if(!Terminated)
{
// tu irgendwas
}
C/C++ Code:
if(!Terminated)
{
  // tu irgendwas
}

_________________
Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.
Unregistrierter





Beitrag Unregistrierter 12:11:46 08.02.2010   Titel:              Zitieren

audacia schrieb:
Zur Situation mit FreeOnTerminate=true kann ich sagen, daß dein Ansatz dann nicht ganz sicher ist:
C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
__fastcall TForm1::~TForm1(void)
{
    FMyThread->Terminate(); // -> der Thread kann ab sofort jederzeit terminiert
                            // werden

    FMyThread->WaitFor(); // ... und was, wenn das hier schon passiert ist? Dann
                          // hat sich das Objekt bereits selbst gelöscht - fatal.

    delete FMyThread; // Noch viel schlimmer - wir zerstören es zweimal.
    FMyThread = NULL;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
__fastcall TForm1::~TForm1(void)
{
FMyThread->Terminate(); // -> der Thread kann ab sofort jederzeit terminiert
// werden

FMyThread->WaitFor(); // ... und was, wenn das hier schon passiert ist? Dann
// hat sich das Objekt bereits selbst gelöscht - fatal.

delete FMyThread; // Noch viel schlimmer - wir zerstören es zweimal.
FMyThread = NULL;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
__fastcall TForm1::~TForm1(void)
{
    FMyThread->Terminate(); // -> der Thread kann ab sofort jederzeit terminiert
                            // werden

    FMyThread->WaitFor(); // ... und was, wenn das hier schon passiert ist? Dann
                          // hat sich das Objekt bereits selbst gelöscht - fatal.

    delete FMyThread; // Noch viel schlimmer - wir zerstören es zweimal.
    FMyThread = NULL;
}
FreeOnTerminate und Terminate()/WaitFor() schließen einander praktisch aus. Mehr noch: wenn ein Thread FreeOnTerminate=true verwendet, darfst du, sobald er läuft, eigentlich nicht mehr von außen auf das Thread-Objekt zugreifen - du weißt nie, ob es überhaupt noch existiert. In diesem Fall ist es notwendig, sich auf andere Kommunikationsmechanismen zu beschränken.

Du hättest beim Beispiel aus dem ersten Post bleiben sollen. :) Das ist die ursprüngliche Fassung des Threads, der das Verhalben mit diesem Minimalbeispiel zeigt. Und dort ist FreeOnTerminate = false. Bei dem späteren Beispiel hatte ich dann in meienr Verzweiflung alles mögliche getestet, unter anderem auch, was ich so an Hinweisen im Netz gefunden habe... Bis hin zum Inherited.


audacia schrieb:

Eine 10-15s anhaltende Verzögerung konnte ich nicht feststellen. Ich habe allerdings nur dein erstes Beispiel ausführlich getestet. Ist das mit dem zweiten besser reproduzierbar, und kannst du noch etwas ins Detail gehen hinsichtlich der notwendigen Randbedingungen (u.a. C++Builder-Version)?


Das ist im BCB 6 und mit dem Code aus dem ersten Post ist das Problem reproduzierbar. Ich habe das Problem ja auch schlußendlich lösen können, auf Basis des Codes meinem zweiten Post. Allerdings nicht mir VCL-Mitteln, sondern eben mit Events. Keine Wartezeit am Ende, Destruktor der Threads wird aufgerufen (was nach Terminate() auch nicht der Fall ist). Und wie gesagt, die Probleme tauchen nicht im laufenden Betrieb auf, sondern nur, wenn die Anwendung beendet wird und man noch aufräumen möchte, sprich die Threads beenden und freigeben.
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 13:57:06 09.02.2010   Titel:              Zitieren

Joe_M. schrieb:
Das ist im BCB 6 und mit dem Code aus dem ersten Post ist das Problem reproduzierbar.
Dann mache ich irgendetwas falsch.

Kannst du evtl. mal ein vollständiges Projekt hochladen?

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
Unregistrierter





Beitrag Unregistrierter 14:29:39 09.02.2010   Titel:              Zitieren

Na klar. Hier ist ein Minimalprojekt: LINK ENTFERNT

Sogar ohne, dass da sonst irgendetwas passiert, dauert der Destruktoraufruf von Form1 ca. 7 Sekunden.

Das alte Projekt habe ich in dieser Form nicht mehr, sondern nur noch die auf Events umgestellte Fassung. Aber die funktioniert ja klaglos und das Beenden und Löschen der Threads bewegt sich im Millisekundenbereich...

Edit: Link entfernt.


Zuletzt bearbeitet von Unregistrierter am 16:12:50 09.02.2010, insgesamt 1-mal bearbeitet
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 16:01:41 09.02.2010   Titel:              Zitieren

Verrückt - ich habe das jetzt mehrere Male durchlaufen lassen, und ich kann es nach wie vor nicht reproduzieren.

Die einzige wahrnehmbare Verzögerung sind die 200ms des Sleep() in den Hintergrundthreads; das kann man durch Vergrößerung der Verzögerung entsprechend skalieren. Aber das sind maximal 0.8s, in der Praxis meist nur 0.4.

Was passiert denn während dieser 7s-Verzögerungen, bzw. wo genau treten sie auf (-> ohne Packages und mit Debug-Bibliotheken kompilieren)? Treten sie überhaupt auf, wenn du schrittweise durch den Code gehst?

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
Unregistrierter





Beitrag Unregistrierter 16:22:28 09.02.2010   Titel:              Zitieren

Die Verzögerung tritt beim WaitFor() auf. Wenn ich das rausnehme, tritt die Verzögerung beim delete auf. Und ja, die Verzögerung habe ich auch beim einzelnen durchsteppen.

Einstellung sind die Standardeinstellungen für ein neues Projekt im BCB 6 (respektive genau so, wie in dem hochgeladenen Projekt).
Wenn ich auf 'Endgültig', ohne Laufzeit-Packages und ohne dynamische RTL kompiliere, verdoppelt sich die Wartezeit fast (auf 12 bis 16 Sekunden). Spontan hätte ich gesagt, dass sich die Zeit eher etwas reduziert?!?

Mit welcher Version testest Du?
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 16:55:53 09.02.2010   Titel:              Zitieren

C++Builder 6; ich hab es aber auch mit C++Builder 2010 getestet, und es verhält sich da nicht anders. (Windows XP SP3.)

Wenn du mit Debug-Bibliotheken kompilierst und während der 7s-Verzögerung das Programm unterbrichst, wird der Call-Stack vermutlich auf eine Zeile von TThread.WaitFor verweisen. Welche ist das genau?
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function TThread.WaitFor: LongWord;
{$IFDEF MSWINDOWS}
var
  H: THandle;
  WaitResult: Cardinal;
  Msg: TMsg;
begin
  H := FHandle;
  if GetCurrentThreadID = MainThreadID then
  begin
    WaitResult := 0;
    repeat
      { This prevents a potential deadlock if the background thread
        does a SendMessage to the foreground thread }
      if WaitResult = WAIT_OBJECT_0 + 1 then
        PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
      Sleep(0);
      CheckSynchronize; // <-- das hier?
      WaitResult := MsgWaitForMultipleObjects(1, H, False, 0, QS_SENDMESSAGE); // <-- oder das hier?
      Win32Check(WaitResult <> WAIT_FAILED);
    until WaitResult = WAIT_OBJECT_0;
  end else WaitForSingleObject(H, INFINITE); // <-- oder gar das hier? (wäre seltsam, da ich GetCurrentThreadID = MainThreadID erwarten würde)
  CheckThreadError(GetExitCodeThread(H, Result));
end;
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function TThread.WaitFor: LongWord;
{$IFDEF MSWINDOWS}
var
H: THandle;
WaitResult: Cardinal;
Msg: TMsg;
begin
H := FHandle;
if GetCurrentThreadID = MainThreadID then
begin
WaitResult := 0;
repeat
{ This prevents a potential deadlock if the background thread
does a SendMessage to the foreground thread }
if WaitResult = WAIT_OBJECT_0 + 1 then
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
Sleep(0);
CheckSynchronize; // <-- das hier?
WaitResult := MsgWaitForMultipleObjects(1, H, False, 0, QS_SENDMESSAGE); // <-- oder das hier?
Win32Check(WaitResult <> WAIT_FAILED);
until WaitResult = WAIT_OBJECT_0;
end else WaitForSingleObject(H, INFINITE); // <-- oder gar das hier? (wäre seltsam, da ich GetCurrentThreadID = MainThreadID erwarten würde)
CheckThreadError(GetExitCodeThread(H, Result));
end;
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function TThread.WaitFor: LongWord;
{$IFDEF MSWINDOWS}
var
  H: THandle;
  WaitResult: Cardinal;
  Msg: TMsg;
begin
  H := FHandle;
  if GetCurrentThreadID = MainThreadID then
  begin
    WaitResult := 0;
    repeat
      { This prevents a potential deadlock if the background thread
        does a SendMessage to the foreground thread }
      if WaitResult = WAIT_OBJECT_0 + 1 then
        PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
      Sleep(0);
      CheckSynchronize; // <-- das hier?
      WaitResult := MsgWaitForMultipleObjects(1, H, False, 0, QS_SENDMESSAGE); // <-- oder das hier?
      Win32Check(WaitResult <> WAIT_FAILED);
    until WaitResult = WAIT_OBJECT_0;
  end else WaitForSingleObject(H, INFINITE); // <-- oder gar das hier? (wäre seltsam, da ich GetCurrentThreadID = MainThreadID erwarten würde)
  CheckThreadError(GetExitCodeThread(H, Result));
end;

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 09:37:51 10.02.2010   Titel:              Zitieren

In diesem Forum gibt es anscheinend ein Tutorial für Threads:

http://bcb-tutorial.c-plusplus.de/Thread/artikel5.html

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.
Unregistrierter





Beitrag Unregistrierter 11:59:54 10.02.2010   Titel:              Zitieren

@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.
DocShoe
Mitglied

Benutzerprofil
Anmeldungsdatum: 02.04.2008
Beiträge: 1605
Beitrag DocShoe Mitglied 13:26:51 10.02.2010   Titel:              Zitieren

Joe_M. schrieb:

@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.
Chrisi_K
Mitglied

Benutzerprofil
Anmeldungsdatum: 08.08.2002
Beiträge: 29
Beitrag Chrisi_K Mitglied 16:24:02 10.02.2010   Titel:              Zitieren

Hallo

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.

Gruss Chis
Unregistrierter





Beitrag Unregistrierter 16:42:51 10.02.2010   Titel:              Zitieren

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?
Chrisi_K
Mitglied

Benutzerprofil
Anmeldungsdatum: 08.08.2002
Beiträge: 29
Beitrag Chrisi_K Mitglied 17:05:28 10.02.2010   Titel:              Zitieren

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.

Gruss Chris
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 20:48:47 10.02.2010   Titel:              Zitieren

Chrisi_K schrieb:
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:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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.

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
Killer-Kobold
Mitglied

Benutzerprofil
Anmeldungsdatum: 16.03.2009
Beiträge: 180
Beitrag Killer-Kobold Mitglied 21:57:43 10.02.2010   Titel:              Zitieren

audacia schrieb:

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.


Hast Du mehr als einen CPU-Kern im Rechner?
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 01:54:16 11.02.2010   Titel:              Zitieren

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.)

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 10:32:35 11.02.2010   Titel:              Zitieren

Hallo,

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 10:42:46 11.02.2010, insgesamt 1-mal bearbeitet
audacia
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.02.2005
Beiträge: 3886
Beitrag audacia Mitglied 12:26:35 11.02.2010   Titel:              Zitieren

Kann es sein, daß du den Hinweiskommentar, den der Thread-Wizard erstellt, nicht gelesen hast?

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
// Wichtig: Methoden und Eigenschaften von Objekten der VCL können nur
// in Methoden verwendet werden, die mit Synchronize aufgerufen wurden, z.B.:
//
//      Synchronize(&UpdateCaption);
//
// wobei UpdateCaption so aussehen könnte:
//
//      void __fastcall TMyThread::UpdateCaption()
//      {
//        Form1->Caption = "In Thread aktualisiert";
//      }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
// Wichtig: Methoden und Eigenschaften von Objekten der VCL können nur
// in Methoden verwendet werden, die mit Synchronize aufgerufen wurden, z.B.:
//
// Synchronize(&UpdateCaption);
//
// wobei UpdateCaption so aussehen könnte:
//
// void __fastcall TMyThread::UpdateCaption()
// {
// Form1->Caption = "In Thread aktualisiert";
// }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
// Wichtig: Methoden und Eigenschaften von Objekten der VCL können nur
// in Methoden verwendet werden, die mit Synchronize aufgerufen wurden, z.B.:
//
//      Synchronize(&UpdateCaption);
//
// wobei UpdateCaption so aussehen könnte:
//
//      void __fastcall TMyThread::UpdateCaption()
//      {
//        Form1->Caption = "In Thread aktualisiert";
//      }

_________________
"Hey, it compiles! Ship it!"
C++Builder Pages · Typsichere Format-Strings
sonic_1233
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.06.2008
Beiträge: 287
Beitrag sonic_1233 Mitglied 12:58:54 11.02.2010   Titel:              Zitieren

Ok, vielen Dank.

_________________
Das Problem ist uns bekannt. Wir arbeiten daran und rechnen mit einer baldigen Lösung.


Zuletzt bearbeitet von sonic_1233 am 13:05:54 11.02.2010, insgesamt 1-mal bearbeitet
C/C++ Forum :: VCL (C++ Builder) ::  Wie erstellt man einen neuen Thread?   Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




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.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

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.