Multithreading



  • Du solltest die Interlocked Funktionen verwenden um auf die Variable zuzugreifen, oder aber eine Mutex/CriticalSection um den Zugriff zu synchronisieren.

    LONG volatile variable=0; 
    CWinThread* pThread = AfxBeginThread (thrFunction, &m_Flag, (void*)&variable); 
    
    //in der thr-funktion: 
    UINT thrFunction(LPVOID pParam) 
    { 
    LONG volatile* pFlag = (LONG volatile*) pParam; 
    ::InterlockedIncrement(pFlag); //erhöhe 
    //... 
    }
    
    //auslesen:
    void Foo()
    {
        LONG v = *variable;
        // oder
        LONG v2 = ::InterlockedCompareExchange(variable, 0, 0);
    }
    


  • irgendwas stimmt nicht. bekomme diese fehlermedung:

    'AfxBeginThread' : Durch keine der 2 Ueberladungen kann Parameter 3 vom Typ 'void *' konvertiert werden

    kann (void*)&variable nicht übergeben
    was ist das für ein fehler und wie kann man es sonst lösen?



  • Ok, hab nicht genau geguckt. Der Aufruf "AfxBeginThread (thrFunction, &m_Flag, &variable); " wie du ihn gepostet hast ist ja schon komplett falsch.

    CWinThread* pThread = AfxBeginThread (thrFunction, (void*)&variable);
    


  • Dieser Thread wurde von Moderator/in Jochen Kalmbach aus dem Forum WinAPI in das Forum MFC (Visual C++) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • aha. wie bekomme ich das jetzt hin?

    am liebsten wär mir wenn eine variable aus meinem haupt-thread im erzeugten worker-thread hochgezählt wird. das ist alles was ich möchte.
    gibts da ne elegante / leichte methode dies zu bewerkstelligen?

    wie ich ja bereits versucht zu erklären habe: ich hab meinen hauptthread. da klickt der user auf einen button. in dessen funktion wird der thread gestartet. in ihm soll etwas getan werden. damit der user auf dem laufenden bleibt will ichs ihm anhand einer progressbar anzeigen, wie weit der prozess schon fertig ist. und jetzt suche ich eine möglichkeit wie ich den statuswert (gespeichert in ner variablen) aus dem work-thread in den haupt-thread bekomme. dabei will ich auf schleifen im haupt-thread verzichten, damit der GUI nicht hängt und man auf abbrechen klicken kann



  • PostMessage + WM_USER.
    Ist Plattformabhängig, aber wenn du schon MFC hast ist das auch schon egal.
    Dafür sparst du dir die Synchronisation, weil Windows das dann übernimmt, und du brauchst keinen Timer, weil der Dialog ja ne Message bekommt (die dann natürlich der Thread abarbeitet welcher den Dialog erstellt hat, also normalerweise der Main-Thread).

    Die Variante ist auch ziemlich "Deppensicher", zumindest solange du nicht 1 Mio. Messages pro Sekunde postest. Das lässt sich aber leicht vermeiden, indem du z.B. den Progress in Prozent (integer) angibst, und nur ne Message postest wenn sich der Progress um min. 1% geändert hat.

    Ahja, dazu definierst du dir einfach ne eigene Message ala...

    #define WM_MYPROJECTNAME_UPDATE_PROGRESS (WM_USER+123)
    

    ...und schickst den Progress dann einfach als wParam oder lParam mit. Handler mit...

    ON_MESSAGE(WM_MYPROJECTNAME_UPDATE_PROGRESS, MemberFunctionName)
    

    ...eintragen. Fertig.
    Worauf du aber SEHR acht geben solltest: der Worker-Thread wird wohl Daten brauchen. Wenn diese Daten mit dem Main-Thread "geshared" sind, dann musst (!) du den Zugriff irgendwie Synchronisieren. Wenn beide Threads die gesharten Daten nur (!) lesen ist es nicht notwendig, sonst auf jeden Fall. Ich schreibe das weil du den Eindruck machst nicht viel von Multithreading zu verstehen. Wenn das der Fall ist, dann bitte mach dich zu dem Thema schlau, und mehr als nur 1-2 Stunden Tutorials lesen - das Thema ist SEHR schwierig und komplex. Wenn nicht dann entschuldige bitte den "blöden Hinweis", kann ja nicht wissen was du kannst und was nicht 😉



  • wow, erstmal vielen vielen dank für deine erklärung. 🙂
    dein vorschlag der datenübertragung ist PERFEKT
    hat sicher arbeit gemacht...

    deswegen fällts mir umso schwerer dich um nochwas zu bitten:
    ich verstehe nicht so ganz wie ich das codetechnisch verwirklichen soll. ich bin blutiger anfänger in threads. kannst du mir ein kleines beispiel geben wie das funktioniert?



  • Versuch das mal zu erklären:

    in der StdAfx.h (benutz ich immer, da die überall bekannt ist) definierst Du:

    #define WM_MYPROJECTNAME_UPDATE_PROGRESS (WM_USER+123)
    

    in deiner Dialog-Klasse wo der Statusbalken angezeigt wird:

    BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)
    	//{{AFX_MSG_MAP(CXXXDlg)
            ...
    	//}}AFX_MSG_MAP
            ON_MESSAGE(WM_MYPROJECTNAME_UPDATE_PROGRESS, MemberFunctionName)
    END_MESSAGE_MAP()
    LRESULT MemberFunctionName(WPARAM wParam, LPARAM lParam)
    {
        Dein_Prograss_Menber.SetPos(wParam);
    }
    

    in der *.h Datei deines Dialoges:

    afx_msg LRESULT MemberFunctionName(WPARAM wParam, LPARAM lParam);
    

    start deines Threads:

    CWinThread* pThread = AfxBeginThread (thrFunction, (void*)this->m_hWnd); ( this ist der Zeiger auf deine Dialogklasse in den der Balken beinhaltet ist)
    

    und die Thread-Funktion:

    UINT thrFunction(LPVOID pParam) 
    { 
    //... 
    ::PostMessage((HWND)pParam,WM_MYPROJECTNAME_UPDATE_PROGRESS,der_Wert,0);
    //...
    }
    

    So benutze ich das immer und lauft perfekt (ne um genau zu sein benutze ich oft send Message da wird auf die ausfürung gewartet und erst dann läuft dein Thread weiter. Aber kommt auf den einsatzfall darauf an.

    Gruß Matthias



  • WOW, matthias 😮 🙂
    danke für die tolle erklärung 🙂

    noch eine frage hab ich: welche funktion meinst du mit MemberFunctionName?



  • Oh Sorry mein Fehler (CopyandPaste) thrFunction sollte eigentlich MemberFunctionName heisen.

    UINT MemberFunctionName (LPVOID pParam) 
    { 
    //... 
    ::PostMessage((HWND)pParam,WM_MYPROJECTNAME_UPDATE_PROGRESS,der_Wert,0);
    //...
    }
    

    so jetzt ist es richtig 😉

    gruß Matthias



  • ok, ich habs jetzt so (bitte nochmal kontrollieren):

    //definiert in der StdAfx.h
    #define WM_MYPROJECTNAME_UPDATE_PROGRESS (WM_USER+123)
    
    //definiert in der CTestDlg.h
    UINT thrFunction(LPVOID pParam);
    
    /////////////////////////////////////////////////////////////////////////////
    // CAboutDlg-Dialogfeld für Anwendungsbefehl "Info"
    
    class CAboutDlg : public CDialog
    {
    
    ...
    
    BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
    	//{{AFX_MSG_MAP(CTestDlg)
    	ON_WM_SYSCOMMAND()
    	ON_WM_PAINT()
    	ON_WM_QUERYDRAGICON()
    	ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
    	ON_BN_CLICKED(IDC_BUTTON2, OnStart)
    	ON_NOTIFY(NM_OUTOFMEMORY, IDC_PROGRESS2, OnOutofmemoryProgress2)
    	ON_MESSAGE(WM_MYPROJECTNAME_UPDATE_PROGRESS, thrFunction)
    	//}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    LRESULT MemberFunctionName(WPARAM wParam, LPARAM lParam)
    {
        m_gesamt.SetPos(wParam); //m_gesamt is die membervariable im haupt-thread
    }
    
    ... //etwas weiter unten
    
    UINT thrFunction(LPVOID pParam)
     {
     //tu was im thread
     ::PostMessage((HWND)pParam,WM_MYPROJECTNAME_UPDATE_PROGRESS,der_wert,0);
     }
    
    ... //weiter unten
    
    void CTestDlg::OnStart() 
    {
     CWinThread* pThread = AfxBeginThread (thrFunction, (void*)this->m_hWnd);
    }
    

    weil das geht nicht, er zeigt einen error an (error C2440: 'type cast' : 'unsigned int (__cdecl *)(void )' kann nicht in 'long (__thiscall CWnd::)(unsigned int,long)' konvertiert werden)

    danke



  • so noch mal zum abgucken: 😉

    //definiert in der StdAfx.h
    #define WM_MYPROJECTNAME_UPDATE_PROGRESS (WM_USER+123)
    
    //definiert in der CTestDlg.h
    UINT thrFunction(LPVOID pParam); //aber außerhalb deiner CTestDlg-Klasse
    
    class CTestDlg: public CDialog
    {
    // Konstruktion
    public:
    	CTestDlg(CWnd* pParent = NULL);	// Standard-Konstruktor
    
    ....
    
    	// Generierte Message-Map-Funktionen
    	//{{AFX_MSG(CHilfetestDlg)
    	virtual BOOL OnInitDialog();
    	afx_msg void OnPaint();
    ...
    	afx_msg LRESULT MemberFunctionName(WPARAM wParam, LPARAM lParam); //hier die Message Funktion deklarieren
    	//}}AFX_MSG
    	DECLARE_MESSAGE_MAP()
    };
    
    /////////////////////////////////////////////////////////////////////////////
    // CAboutDlg-Dialogfeld für Anwendungsbefehl "Info"
    
    class CAboutDlg : public CDialog
    {
    
    ...
    
    BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
    	//{{AFX_MSG_MAP(CTestDlg)
    	ON_WM_SYSCOMMAND()
    	ON_WM_PAINT()
    	ON_WM_QUERYDRAGICON()
    	ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
    	ON_BN_CLICKED(IDC_BUTTON2, OnStart)
    	ON_NOTIFY(NM_OUTOFMEMORY, IDC_PROGRESS2, OnOutofmemoryProgress2)
    	ON_MESSAGE(WM_MYPROJECTNAME_UPDATE_PROGRESS, MemberFunctionName) //hier die Funktion die bei der Message ausgeführt werden soll
    	//}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    LRESULT CTestDlg::MemberFunctionName(WPARAM wParam, LPARAM lParam)// die Message Funktion
    {
        m_gesamt.SetPos(wParam); //m_gesamt is die membervariable im haupt-thread
    
    return 0; // hier kannst du deinem Thread etwas zurücksignalisieren oder auch nicht
    }
    
    ... //etwas weiter unten
    
    UINT thrFunction(LPVOID pParam) // das ist der Thread
     {
     //tu was im thread
     ::PostMessage((HWND)pParam,WM_MYPROJECTNAME_UPDATE_PROGRESS,der_wert,0);
    
    return 0; //normales beenden des Threads
     }
    
    ... //weiter unten
    
    void CTestDlg::OnStart() 
    {
     CWinThread* pThread = AfxBeginThread (thrFunction, (void*)this->m_hWnd);
    }
    

    so hab ich das jetzt bei mir laufen und es funzt, aber eben nur ein durchlauf, den Thread solltest du entsprechen ausbauen, eventuell auch mit ner Variable oder so zu frühzeitigen beenden. Falls der Beendet werden soll bevor er runtergelaufen ist. Aber das nur so nebenbei. Viel Spass.

    Gruß Matthias



  • HAMMER
    vielen dank 🙂

    funktioniert super



  • Nachdem ich den Thread jetzt ein wenig verfolgt habe und auch ein ähnliches Problem habe muss ich mich hier mal einklinken. Hoffe Majestix stört es nicht recht, aber nachdem seine Probleme ja anscheinend gelöst sind hoffe ich mal, dass ich den Thread nicht zu unübersichtlich mache!

    Mein Problem:

    Ich möchte aus meiner von CSCrollView abgeleiteten Klasse einen Thread aufrufen, welcher einige Rechenoperationen auf ein paar in CDocument gespeicherten Daten ausführt, die ein wenig länger dauern.

    Nun möchte ich während der Abarbeitung dieser Aufgaben einen Dialog mit einem Fortschrittsbalken anzeigen, was ja eigentlich kein großes Problem sein sollte.

    Mein Problem ist, dass ich in der Thread-Funktion auf einige Member der CScrollView-Klasse zugreifen müsste (wie zB. GetDocument()), dies allerdings nicht kann, da die Thread-Funktion ja ausserhalb der Klasse definiert ist (bzw. statisch sein muss, wenn sie innerhalb der Klasse steckt).

    Wie kann man sowas nun realisieren?

    Mit der Dialog-Sache bin ich mir noch nicht so ganz sicher, also schreibe ich hier mal, wie ich mir das vorstelle und hoffe, dass mir jemand sagt, ob ich mit meinem Vermutungen richtig liege oder wie es richtig wäre:

    Ich habe zB. die Funktion CallThread, in der ich mit AfxBeginThread die Thread-Funktion starte. Nach start der Thread-Funktion lasse ich von der CallThread-Funktion aus den Modalen Dialog anzeigen (dh. hier steht die Anwendung mal solange der Dialog sichtbar ist).

    Aus der Thread-Funktion aktualisiere ich die ProgressBar des Dialogs mittels PostMessage (wie hier bereits ausführlich diskutiert) und schließe ihn, wenn die Abarbeitung fertig ist -> die Funktion CallThread läuft auch wieder weiter, da der Modale Dialog ja geschlossen wurde.

    Hoffe, dass mir jemand helfen kann (speziell die dick geschriebene Frage bereitet mir großes Kopfzerbrechen)!

    danke, mfg

    mike



  • class Foo
    {
    public:
        // ...
    private:
        static UINT ThreadFunctionStatic(LPVOID param)
        {
            Foo* that = static_cast<Foo*>(param);
            return that->ThreadFunction();
        }
    
        UINT ThreadFunction()
        {
            // ...
            return 123;
        }
    };
    

    ThreadFunctionStatic kannst du dann als Thread starten, als "void* param" gibste dem Thread dann einfach "this" mit. Fertig. Aber. Du musst verdammt aufpassen dass du nicht ein einem Thread Daten änderst während der andere die liest, oder, noch schlimmer, auch ändert. Lass das mit den Threads mal lieber 😉



  • Schon klar, dass man da auf die Synchronisation ein wenig aufpassen muss. Nachdem der Benutzer während der Berechnungen sowieso nichts am Programm machen kann/darf hab ich das mit den Threads mal weggelassen und zeige einfach den Fortschritts-Dialog an, welcher halt laufend aktualisiert wird 😉

    Trotzdem gut zu wissen, wie sowas funktioniert!

    danke, mfg

    ps.:

    Lass das mit den Threads mal lieber 😉

    Nana, man ist ja hier um was zu lernen und nicht um gesagt zu bekommen, dass es doch ein wenig zu schwer für einen ist und man lieber die Finger davon lassen sollte 😉



  • suamikim schrieb:

    Nana, man ist ja hier um was zu lernen und nicht um gesagt zu bekommen, dass es doch ein wenig zu schwer für einen ist und man lieber die Finger davon lassen sollte

    Du hast natürlich recht. Ich hätte besser sagen sollen: beschränke den Einsatz von Threads erstmal auf Projekte die nicht sehr kritisch sind, also wos nicht schlimm ist wenn die hin und wieder mal falsche Daten produzieren oder abschmieren. Lernen musst du ja irgendwie, und das tut man natürlich ab einem gewissen Punkt immer noch am besten in der Praxis.

    Und auch wenn jmd. sich schon wirklich gut mit Multithreading auskennt würde ich empfehlen den Einsatz mehrerer Threads in "kritischen" Projekten 3x zu überdenken. Threads sind böse, und wenn du 1x nicht hinguckst packen sie dich von hinten, und verprügeln dich mit einem tiefgefrohrenen Murphy 😃



  • hustbaer schrieb:

    Du hast natürlich recht. Ich hätte besser sagen sollen: beschränke den Einsatz von Threads erstmal auf Projekte die nicht sehr kritisch sind, also wos nicht schlimm ist wenn die hin und wieder mal falsche Daten produzieren oder abschmieren. Lernen musst du ja irgendwie, und das tut man natürlich ab einem gewissen Punkt immer noch am besten in der Praxis.

    Wäre natürlich passender gewesen. Aber keine Angst, ich hab das schon nicht in den falschen Hals bekommen und es so verstanden, wie ich es verstehen wollte 😉

    hustbaer schrieb:

    Und auch wenn jmd. sich schon wirklich gut mit Multithreading auskennt würde ich empfehlen den Einsatz mehrerer Threads in "kritischen" Projekten 3x zu überdenken. Threads sind böse, und wenn du 1x nicht hinguckst packen sie dich von hinten, und verprügeln dich mit einem tiefgefrohrenen Murphy 😃

    Ich denke das ich schon ein wenig Erfahrung mit Threads habe, allerdings "leider" erst in C#. Hab dort ein relativ großes (kritisches) Projekt realisiert und konnte in einigen Bereichen gar nicht anders, als auf Threads zurückgreifen, was mir (so denke ich) ganz gut gelungen ist.

    Also das Grundverständnis sollte eigentlich da sein, nur bei der Umsetzung scheitert es halt Momentan noch ein wenig aufgrund der ungewohnten Sprache, aber das wird schon.

    Genug von dem Thema. Mein Problem hab ich ja jetzt anders gelöst und sollte ich wiedereinmal eine solche Lösung benötigen steht hier ja schon eine (hoffentlich) funktionierende Antwort!

    danke, mfg


Anmelden zum Antworten