Thread-Problem
-
Hi
Ich habe ein Problem mit Threads. In den Threads will ich eine Funktion zum synchonisieren aufrufen, der aber noch einen String übergeben werden soll:
void __fastcall FindBlogs::updatetext(AnsiString code) { Form1->Memo1->Text = code; }
AnsiString meinString= "irgendwas"; Synchronize(&updatetext(blub));
Ich bekomme diesen Fehler:
[C++ Error] Unit2.cpp(64): E2109 Not an allowed type
Weiß jemand was ich falsch mache ?
-
Ja, das ist eine nervige Sache... Man kann keine Parameter übergeben. Punkt.
Mach den AnsiString zu einer Membervariable des Threads, damit er in beiden Funktionen zur Verfügung steht.
Gruß KK
-
In Delphi gibt es dafür praktischerweise anonyme Methoden, dort sähe das etwa so aus:
type meinString: String; begin Synchronize (procedure begin UpdateText (meinString) end);
In C++ gibt es das leider (noch) nicht; einstweilen kannst du aber ein Funktionsobjekt zu solchem Zwecke benutzen. Man könnte sich sogar eine Wrapperklasse schreiben, die die Parameterübergabe einigermaßen annehmlich macht (das wäre mal wieder ein Fall für Variadic Templates - leider unterstützt BCC die noch nicht). Zufällig hatte ich gerade Lust dazu:
class TCppThread; namespace detail { enum NoType { ntNoVal }; enum SyncType { stSynchronized, stQueued, }; template <typename T> struct RefWrapper { T val; RefWrapper (void) {} RefWrapper (const T& _val) : val (_val) {} operator T (void) { return val; } }; template <typename T> struct RefWrapper <T&> { T* val; RefWrapper (void) {} RefWrapper (T& _val) : val (&_val) {} operator T& (void) { return *val; } }; template <typename T, typename FuncObjT, typename R, typename T1 = NoType, typename T2 = NoType, typename T3 = NoType> struct SyncExchangeData { private: T* self; FuncObjT funcObj; R r; RefWrapper <T1> p1; RefWrapper <T2> p2; RefWrapper <T3> p3; T* cast (TCppThread* _self) { return &dynamic_cast <T&> (*_self); } public: SyncExchangeData (TCppThread* _self, FuncObjT _funcObj) : self (cast (_self)), funcObj (_funcObj) {} SyncExchangeData (TCppThread* _self, FuncObjT _funcObj, T1 _p1) : self (cast (_self)), funcObj (_funcObj), p1 (_p1) {} SyncExchangeData (TCppThread* _self, FuncObjT _funcObj, T1 _p1, T2 _p2) : self (cast (_self)), funcObj (_funcObj), p1 (_p1), p2 (_p2) {} SyncExchangeData (TCppThread* _self, FuncObjT _funcObj, T1 _p1, T2 _p2, T3 _p3) : self (cast (_self)), funcObj (_funcObj), p1 (_p1), p2 (_p2), p3 (_p3) {} template <typename> void __fastcall doSyncCall0 (void) { r = (self->*funcObj) (); } template <typename> void __fastcall doSyncCall1 (void) { r = (self->*funcObj) (p1); } template <typename> void __fastcall doSyncCall2 (void) { r = (self->*funcObj) (p1, p2); } template <typename> void __fastcall doSyncCall3 (void) { r = (self->*funcObj) (p1, p2, p3); } R getResult (void) { return r; } }; template <typename T, typename FuncObjT, typename T1 = NoType, typename T2 = NoType, typename T3 = NoType> struct SyncExchangeData <T, FuncObjT, void, T1, T2, T3> { private: T* self; FuncObjT funcObj; RefWrapper <T1> p1; RefWrapper <T2> p2; RefWrapper <T3> p3; T* cast (TCppThread* _self) { return &dynamic_cast <T&> (*_self); } public: SyncExchangeData (TCppThread* _self, FuncObjT _funcObj) : self (cast (_self)), funcObj (_funcObj) {} SyncExchangeData (TCppThread* _self, FuncObjT _funcObj, T1 _p1) : self (cast (_self)), funcObj (_funcObj), p1 (_p1) {} SyncExchangeData (TCppThread* _self, FuncObjT _funcObj, T1 _p1, T2 _p2) : self (cast (_self)), funcObj (_funcObj), p1 (_p1), p2 (_p2) {} SyncExchangeData (TCppThread* _self, FuncObjT _funcObj, T1 _p1, T2 _p2, T3 _p3) : self (cast (_self)), funcObj (_funcObj), p1 (_p1), p2 (_p2), p3 (_p3) {} template <typename> void __fastcall doSyncCall0 (void) { (self->*funcObj) (); } template <typename> void __fastcall doSyncCall1 (void) { (self->*funcObj) (p1); } template <typename> void __fastcall doSyncCall2 (void) { (self->*funcObj) (p1, p2); } template <typename> void __fastcall doSyncCall3 (void) { (self->*funcObj) (p1, p2, p3); } void getResult (void) { } }; } // namespace detail class TCppThread : public TThread { protected: template <typename T, typename R> R SynchronizedCall (R (T::* func) (void)) { #ifdef __CODEGEARC__ static_assert (__is_base_of (TCppThread, T), "T is not a base of TCppThread"); #endif typedef R (T::* FuncObjT) (void); detail::SyncExchangeData <T, FuncObjT, R> sed (this, func); Synchronize (sed.doSyncCall0 <R>); return sed.getResult (); } template <typename T, typename R, typename T1, typename TT1> R SynchronizedCall (R (T::* func) (T1), TT1 p1) { #ifdef __CODEGEARC__ static_assert (__is_base_of (TCppThread, T), "T is not a base of TCppThread"); #endif typedef R (T::* FuncObjT) (T1); detail::SyncExchangeData <T, FuncObjT, R, T1> sed (this, func, p1); Synchronize (sed.doSyncCall1 <R>); return sed.getResult (); } template <typename T, typename R, typename T1, typename TT1, typename T2, typename TT2> R SynchronizedCall (R (T::* func) (T1, T2), TT1 p1, TT2 p2) { #ifdef __CODEGEARC__ static_assert (__is_base_of (TCppThread, T), "T is not a base of TCppThread"); #endif typedef R (T::* FuncObjT) (T1, T2); detail::SyncExchangeData <T, FuncObjT, R, T1, T2> sed (this, func, p1, p2); Synchronize (sed.doSyncCall2 <R>); return sed.getResult (); } template <typename T, typename R, typename T1, typename TT1, typename T2, typename TT2, typename T3, typename TT3> R SynchronizedCall (R (T::* func) (T1, T2, T3), TT1 p1, TT2 p2, TT3 p3) { #ifdef __CODEGEARC__ static_assert (__is_base_of (TCppThread, T), "T is not a base of TCppThread"); #endif typedef R (T::* FuncObjT) (T1, T2, T3); detail::SyncExchangeData <T, FuncObjT, R, T1, T2, T3> sed (this, func, p1, p2, p3); Synchronize (sed.doSyncCall3 <R>); return sed.getResult (); } public: __fastcall TCppThread (bool CreateSuspended) : TThread (CreateSuspended) {} };
Anwendung wie folgt:
class TMyThread : public TCppThread { private: int foo (void) { return 42; } String bar (String s) { return "foo=" + s; } void baz (int i, String& s) { s = String (i); } public: TMyThread (void) : TCppThread (false) {} void __fastcall Execute (void) { int fooResult = SynchronizedCall (&TMyThread::foo); String barResult = SynchronizedCall (&TMyThread::bar, "bar"); String bazResult; SynchronizedCall (&TMyThread::baz, 42, static_cast <String&> (bazResult)); assert (fooResult == 42); assert (barResult == "foo=bar"); assert (bazResult == "42"); } };
Getestet mit C++Builder 2010, sollte aber auch mit früheren Versionen funktionieren.
-
Das Programm schnurrt jetzt wie ein Kätzchen.
Wenn ich jedoch den Butten zum Thread-start drücke schmiert das Programm ab:Unit1.cpp:
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit1.h" #include "Unit2.h" #include "Unit2.cpp" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { new FindBlogs(false); }
Unit2.Cpp:
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit2.h" #include "Unit1.h" #pragma package(smart_init) AnsiString puffer = ""; TStringList* text = new TStringList(); //<- Die globale Stringlist die nachher //noch in Unit 1.cpp gebraucht wird //--------------------------------------------------------------------------- __fastcall FindBlogs::FindBlogs(bool CreateSuspended) : TThread(CreateSuspended) { FreeOnTerminate; } void __fastcall FindBlogs::updatetext() { } //--------------------------------------------------------------------------- void __fastcall FindBlogs::Execute() { AnsiString keyword = Form1->Edit1->Text; AnsiString seiten = StrToInt(Form1->Edit2->Text); } }
HIER
AnsiString keyword = Form1->Edit1->Text; AnsiString seiten = StrToInt(Form1->Edit2->Text);
scheint der Fehler ausgelöst zu werden. Wenn die Daten aus Form1 in den Thread übergeben werden crasht das Programm...
-
Diese Linker-warnungen bekomme ich:
[Linker Warning] Public symbol '_puffer' defined in both module C:\\UNIT1.OBJ and
C:\\UNIT2.OBJ[Linker Warning] Public symbol '_text' defined in both module
C:\\UNIT1.OBJ and
C:\\UNIT2.OBJ
-
Du musst auch Synchronize() verwenden, wenn Du Daten aus Form1 liest, nicht nur beim Schreiben.
Und was soll das FreeOnTerminate im Konstruktor? So bewirkt das rein gar nichts. Du musst das entweder auf true oder false setzen.
Was ist denn der Zweck der beiden Variablen, bei denen Du die Linkerwarnung bekommst? Wenn Du sie jeweils in den Units brauchst, mach sie private. Wenn die zum Datenaustausch zwischen den Units gedacht sind, brauchst Du die nur einmal.
-
Die StringList brauche ich in Unit1 und Unit 2. Dann werde ich die puffer-variable mal private deklarieren.
void __fastcall FindBlogs::SyncText() { sourcecode = puffer; start= 1; Form1->Memo1->Text = "text in puffer !"; }
Hier setze ich die globale Variable start auf 1, wenn "sourcecode" einen bestimmt String enthält. Mein Ziel ist jetzt, einen zweiten Thread zu starten, der so lange wartet bis start auf 1 gesetzt wird:
_
public: __fastcall FindBlogs(bool CreateSuspended); }; int start = 0; AnsiString puffer = ""; TStringList* text = new TStringList();
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit3.h" #include "Unit2.h" #include "Unit1.h" #pragma package(smart_init) //--------------------------------------------------------------------------- __fastcall SentThread::SentThread(bool CreateSuspended) : TThread(CreateSuspended) { FreeOnTerminate = true; } //--------------------------------------------------------------------------- void __fastcall SentThread::startposting() { Form1->Memo1->Text = "Start posting !"; } //--------------------------------------------------------------------------- void __fastcall SentThread::Execute() { while(1) { if(start> 0) { Synchronize(&startposting); } Idglobal::Sleep(0); } } //---------------------------------------------------------------------------
Was passiert ? garnix...
Thread 1 und 2 werden aus dem Hauptthread gestartet. Thread 1 setzt "start" auf 1. In Thread 2 wird aber nicht erkannt, dass start auf 2 gesetzt wurde.
Auf jeden Fall springt er nicht in die if-schleife rein.
-
Mal von den Logikfehlern abgesehen. Du MUSST jeglichen Datenaustausch Threadsicher gestalten. Du darfst nicht einfach wild Variablen aus anderen Threads lesen und/oder schreiben. Siehe TCriticalSection (mir persönlich gefält das Pendant aus der WinAPI besser), oder eine der anderen Möglichkeiten.
Um mitzuteilen, dass ein bestimmter Status eingetreten ist, verwendet man das Botschaftssystem, oder eine der anderen diversen Möglichkeiten, zB Events oder Semaphores in Verbindung mit WaitForSingleObject oder WaitForMultipleObjects. Niemals, ich wiederhole, niemals! pollt man auf einen Status, das verbraucht einfach nur unnötig CPU-Zeit (und das nicht unerheblich).
Gruß KK
-
Ich habe deine Tipps befolgt und das ganze umgeschrieben:
Unit2.cpp:
void __fastcall FindBlogs::SyncText() { sourcecode = puffer; Form1->Memo1->Text = "text in puffer !"; SetEvent ( hEvent ); } void __fastcall FindBlogs::updatetext() { Form1->Label1->Caption = "Threads running: " + IntToStr(threadsrunning); } void __fastcall FindBlogs::GetInput() { AnsiString keyword = Form1->Edit1->Text; int seiten = StrToInt(Form1->Edit2->Text); } //--------------------------------------------------------------------------- void __fastcall FindBlogs::Execute() { HANDLE hEvent = CreateEvent ( NULL, FALSE, FALSE, "quelltext"); threadsrunning++; Synchronize(&updatetext); Synchronize(&GetInput); Synchronize(&SyncText); DeleteThread(); }
Unit3.cpp:
//--------------------------------------------------------------------------- __fastcall SentThread::SentThread(bool CreateSuspended) : TThread(CreateSuspended) { FreeOnTerminate = true; } //--------------------------------------------------------------------------- void __fastcall SentThread::startposting() { Form1->Memo1->Text = "Start posting !"; } //--------------------------------------------------------------------------- void __fastcall SentThread::Execute() { while(WaitForSingleObject ( hEvent, INFINITE) != WAIT_OBJECT_0) { } Synchronize(&startposting); }
"hEvent" ist global in Unit2.cpp deklariert:
#ifndef Unit2H #define Unit2H //--------------------------------------------------------------------------- #include <Classes.hpp> //--------------------------------------------------------------------------- class FindBlogs : public TThread { private: AnsiString keyword; int seiten; protected: ....funktionen public: .............. }; HANDLE hEvent; AnsiString puffer = ""; TStringList* text = new TStringList();
-
Sorry für das Doppelposting. Ich kann den Beitrag leider nicht editieren:
Das ganze funktioniert so anscheinend auch nicht. Er springt nicht mehr aus der
while(WaitForSingleObject -schleife im Thread 3 raus.
-
So langsam kann ich das Problem eingrenzen.
Wenn ich HANDLE ehEvent; in der Unit 2 deklariere funktioniert das ganze wunderbar.
Deklariere ich aber HANDLE hEvent in der unit2.h ist hEvent nach dem Aufruf von CreateEvent() NULL.