Threadfunktion



  • Hallo,

    wie genau deklariere ich eine Threadfunktion?

    Ich habe bis jetzt immer nur folgenden Funktionsaufruf:

    unsigned long __stdcall MoveFiles(LPVOID Data)
    

    Diesen setze ich meist vor der Thread aufrufenden Funktion, damit ich die Deklaration umgehe.

    Denn wenn ich ihn einfach so deklariere, klappt dies nicht:

    unsigned long __stdcall MoveFiles(LPVOID Data);
    

    Könnte man auch andere Rückgabetypen bestimmen, wie z.B. void?
    Ist es möglich auch andere oder mehr Übergabe Parameter anzugeben??
    Oder wie könnte man z.B. mehrere AnsiStrings dieser Funktion übergeben?

    MfG TheCaleb



  • Wenn Du den Thread mit "_beginthreadex" erzeugst (was auch empfohlen wird, dann ist das ok...
    Mit CreateThread, dann

    DWORD WINAPI MyThreadProc(LPVOID pData);
    

    Übergeben kannst Du beliebig viele Daten. Du musst diese Daten nur in einen eigene Struktur legen und dann übergibst Du einen Zeiger auf diese Struktur. Dieser Zeiger wird dann in "pData" dem Thread mitgeteilt. Hier musst Du dann noch auf die Structur zurück-casten und schon hast Du Deine Daten wieder.



  • Hmmmm also ich habe nun mein Funktionsaufruf so geschrieben:

    LPTHREAD_START_ROUTINE Warten2 = &Warten;
    Warteschleife=CreateThread(NULL, 0, Warten2, NULL, 0, &ThreadID);
    

    und den Funktionskopf so:

    DWORD WINAPI Warten(void *)
    

    Doch, was mache ich nun falsch??


  • Mod

    TheCaleb schrieb:

    Hmmmm also ich habe nun mein Funktionsaufruf so geschrieben:

    LPTHREAD_START_ROUTINE Warten2 = &Warten;
    Warteschleife=CreateThread(NULL, 0, Warten2, NULL, 0, &ThreadID);
    

    und den Funktionskopf so:

    DWORD WINAPI Warten(void *)
    

    Doch, was mache ich nun falsch??

    Warum so umständlich?

    Warteschleife=CreateThread(NULL, 0, Warten, NULL, 0, &ThreadID);
    

    BTW: Wenn Du die CRT verwendest sollte man _beginthreadex verwenden!
    Das hat doch Jochen auch schon geraten!



  • Moment mal!

    Also wenn ich die Funktion mit

    Warteschleife=_beginthreadex(NULL, 0, Warten, NULL, 0, &ThreadID);
    

    aufrufe, wie sollte dann der Funktionskopf aussehen?

    Ich komme hier gerade nicht mehr klar! 🙄

    Axo eine Nebenfrage, wie genau macht man das nochmal mit dem Casten?
    Also beim Funktionsaufruf gibt man einfach die erzeugte Struktur mit & voher an, um die Adresse zu bekommen, doch wie castet man die in dem Thread wieder zurück um die Werte zu bekommen??



  • TheCaleb schrieb:

    Doch, was mache ich nun falsch??

    Frag deinen Compiler, der wird's dir schon sagen (und ich kenne keinen Compiler, der bei Problemen keine genaue Fehlermeldung liefert :)).

    PS: _beginthreadex erwartet eine:

    unsigned __stdcall ThreadFunc(void* param);
    

    Und das mit dem Casten funktioniert so:

    unsigned __stdcall ThreadFunc(void* param)
    {
      ThData* pdata = static_cast<ThData*>(param);
      ...
    }
    
    ...
    ThData data;
    unsigned thread_id;
    unsigned long status = _beginthreadex(NULL,0,ThreadFunc,&data,0,&thread_id);
    


  • Vielen Dank, doch wie kann ich den mit _beginthreadex erzeugten Thread wieder killen?
    Bei CreateThread wurde ja ein Handle zurück gegeben mit dem das ging, oder irre ich mich da??



  • Und wenn ich die Funktion mit

    unsigned __stdcall Warten(void*);
    

    deklariere und die alles andere nach CStolls Beispiel mache, meldet mir der Compiler:

    [C++ Error] Unit1.cpp(82): E2034 Cannot convert 'unsigned int (__stdcall * (_closure )(void *))(void *)' to 'unsigned int (__stdcall *)(void *)'

    [C++ Error] Unit1.cpp(82): E2342 Type mismatch in parameter '__start' (wanted 'unsigned int (__stdcall *)(void *)', got 'void')


  • Mod

    TheCaleb schrieb:

    Und wenn ich die Funktion mit

    unsigned __stdcall Warten(void*);
    

    deklariere und die alles andere nach CStolls Beispiel mache, meldet mir der Compiler:

    [C++ Error] Unit1.cpp(82): E2034 Cannot convert 'unsigned int (__stdcall * (_closure )(void *))(void *)' to 'unsigned int (__stdcall *)(void *)'

    [C++ Error] Unit1.cpp(82): E2342 Type mismatch in parameter '__start' (wanted 'unsigned int (__stdcall *)(void *)', got 'void')

    Zeige uns doch einfach Code. Das ist so einfach nur fischen im Trüben. Ichhabe das Gefühl Du hast wieder einen Adressoperator zuviel!


  • Mod

    TheCaleb schrieb:

    Vielen Dank, doch wie kann ich den mit _beginthreadex erzeugten Thread wieder killen?
    Bei CreateThread wurde ja ein Handle zurück gegeben mit dem das ging, oder irre ich mich da??

    Man killt keinen Thread! Ein Thread beendet sich, indem er einfach returniert!
    Und das erreicht man einfach in dem man ein Flag/Event setzt un der Thread dieses Flag/Event erkennt und einfach returniert.



  • TheCaleb schrieb:

    Vielen Dank, doch wie kann ich den mit _beginthreadex erzeugten Thread wieder killen?
    Bei CreateThread wurde ja ein Handle zurück gegeben mit dem das ging, oder irre ich mich da??

    Ganz einfach: der letzte Parameter von beginthreadex ist ein Zeiger auf eine unsigned-Variable - dort packt die Funktion deine Thread-ID rein.
    (aber es ist sowieso keine gute Idee, einen Thread von außen zu killen. Schick ihm lieber eine Nachricht, daß er sich beenden soll (du könntest z.B. einen Member der ThData-Struktur als Ende-Flag setzen))

    Zu der Fehlermeldung: Dann hast du es offenbar nicht so gemacht wie oben - zeig doch mal den genauen Code.



  • Wie soll ich das denn mit den Flags genau machen?
    Also ich habe in der Warten Funktion eine WaitFor, welche unendlich lange wartet, wenn keine Aktion geschieht.
    Diesen Thread würde ich gerne auch killen können, durch z.B. einem Stop- Button.
    Geht das mit der ID?
    Und wenn ja, wie?

    Wegen den Fehlern:

    ...
    public:		// User declarations
            unsigned __stdcall Warten(void*);
            __fastcall TForm1(TComponent* Owner);
    };
    
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
     ...
     _beginthreadex(NULL, 0, Warten, NULL, 0, &ThreadID);
     ...
    }
    
    unsigned __stdcall TForm1::Warten(void *)
    {
     ...
    }
    


  • Erstens: Callback-Mechanismen haben so ihre (hm) Probleme mit Klassenmethoden (da fehlt einfach der this-Zeiger) - nimm eine globale Funktion oder eine statische Methode.

    Zweitens: Killen von außen ist schlecht. Beenden von innen ist besser:

    class TForm1
    {
      ...
      bool ende;//Endemarke für Thread
      static unsigned __stdcall Warten(void*);//Threadfunktion
      ...
    };
    
    unsigned __stdcall TForm1::Warten(void *param)
    {
      TForm1* pthis = static_cast<TForm1*>(param);
      while(!pthis->ende)
      {
        ...//mach was
      }
    }
    
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner), ende(false)
    {
      ...
    
      _beginthreadex(NULL, 0, Warten, this, 0, &ThreadID);
      ...
    }
    
    int TForm1::OnButtonStop()
    {
      ende=true;//<- dadurch wird die Schleife in der Thread-Funktion SAUBER beendet
    }
    


  • Danke aber was genau soll
    [C++ Error] Unit1.cpp(173): E2231 Member TForm1::FileListBox1 cannot be used without an object
    heißen?
    Denn diese Meldung erscheint nun...



  • Achso und gibt es denn eine Funktion, mit der man einen Thread mit Hilfe der ID killen kann??



  • TheCaleb schrieb:

    Danke aber was genau soll
    [C++ Error] Unit1.cpp(173): E2231 Member TForm1::FileListBox1 cannot be used without an object
    heißen?
    Denn diese Meldung erscheint nun...

    Dadurch, daß die Threadfunktion statisch ist, kann sie nicht mehr direkt auf "normale" Member zugreifen (eine Einschränkung gibt's immer). Also mußt du indirekt zugreifen über pthis->FileListBox1 .

    Und wenn du unbedingt den Thread von außen killen willst, nimm TeminateThread (mit dem Rückgabewert von _beginthreadex() als Thread-Handle).

    Aber auch auf die Gefahr hin, mich zu wiederholen: TU'S LIEBER NICHT. Auf diese Weise wird der Thread SOFORT abgeschossen, ohne die Chance auf irgendwelche Aufräumarbeiten. Wenn du Glück hast, bleibt nur ein Speicherleck, aber mit etwas Pech blockierst du dir damit das gesamte Programm. Die bessere Lösung ist es, den Thread sanft einschlafen zu lassen (Code siehe oben) - das Hauptprogramm setzt 'ende' auf true und der Thread erkennt diese Änderung bei nächster Gelegenheit und beendet sich selbst (inklusive Aufräumarbeiten).



  • Ist ok, dann werde ich die sanfte Methode von dir einbauen!

    Ähm wie kann ich denn diese this in

    _beginthreadex(NULL, 0, Warten, this, 0, &ThreadID);
    

    und

    TForm1* pthis = static_cast<TForm1*>(param);
    

    auf mehrere Funktionen anwenden??
    Also die Warten Funktion ruft ein weitern Thread auf, wo die FileListBox1 gebraucht wird.

    Ich habe das nun so gemacht, doch das geht nicht:

    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
       ...
       _beginthreadex(NULL, 0, Warten, this, 0, &ThreadID);
       ...
    }
    
    unsigned __stdcall TForm1::Warten(void *param)
    {
       TForm1* pthis = static_cast<TForm1*>(param);
       ...
       _beginthreadex(NULL, 0, MoveFiles,pthis, 0, &ThreadID);
    }
    
    unsigned __stdcall TForm1::MoveFiles(void *param)
    {
       TForm1* pthis = static_cast<TForm1*>(param);
       ...
       pthis->FileListBox1->Update();
       ... 
    }
    


  • Bist du sicher, daß es nötig ist, so mit Threads um sich zu werfen?



  • Ja ich brauche mehrere Threads!
    Wie mache ich das denn, dass ich dem anderen Thread ebenfalls einen Zufgiff auf die FileListBox1 geben kann, ohne ein Fehler zu erzeugen?



  • Wenn es dir nur um den Compiler geht, ist das oben der richtige Weg. Allerdings kenne ich es zumindest von MFC, daß du nicht Thread-übergreifend auf Fenster und Dialogitems zugreifen kannst (wie das bei Borland aussieht, mußt du die Experten fragen). D.h. die Arbeitsthreads sollten per SendMessage() eine Nachricht an den Haupt-(=Anzeige-)Thread senden, der sich dann um die Darstellung kümmern kann.



  • EIgentlich hat es nichts mit MFC oder Borland zu tun, sondern *darf* aus Gründen der WinAPI nicht Thread-übergreifend auf andere Fenster zugreifen!
    Bei einigen Nachrichten geht es wohl (zufällig), aber ob dies in Vista oder späteren OS-Version der Fall ist, ist MS überlassen. Es ist dokumentiert, dass man es nicht *darf* (kann gerade nur die Stelle nicht finden 😉 )


Anmelden zum Antworten