Multithreading
-
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