ThreadProblem
-
Besser nicht - sowas führt zu Poblemen, wenn gerade beide Seiten gleichzeitig damit hantieren wollen. Für alles, was zwischen den Threads ausgetauscht werden soll, brauchst du Synchronisationsmechanismen (z.B. über einen Mutex, den jeder vor Verwendung lockt).
-
Also nix mit Thread starten und gut ist... mist

Naja komm ich nicht drum rum. Muss ja gemacht werden.Hast du irgendwo einen Codeschnipsel (oder nen Thread hier im Forum), der das mal verdeutlich könnte?
Ich hab nämlich gedacht
AfxBeginThread( <FUNCTION>,(this),THREAD_PRIORITY_NORMAL,0,0 );einbauen und es geht (jedenfalls das das im eigenen Thread läuft).
-
Wenn du die Suchfunktion benutzt, wirst du schon einiges finden - hoffe ich

Ansonsten: Ja, letztenendes ist die Sache so einfach - nachdem du deine Threadfunktion geschrieben hast:
static UINT <Funktion>(LPVOID param) { my_type * pThis = (my_type*)(param); //von jetzt an kannst du über pThis->... auf die Member des Dialogs zugreifen ... }(Zur Synchronisation solltest du dir mal CMutex etc. und CSingleLock ansehen:
//in der Klasse: CMutex m_sync; //in der Thread-Funktion (und im Hauptprogramm) { CSingleLock lock(pThis->m_sync,TRUE); //Zugriffe auf andere Memberdaten ... }//der Dtor gibt den Mutex wieder frei
-
Du solltest bei "shared data" Multithreading auf ein paar Sachen achtgeben:
-
So wenig wie möglich gemeinsam verwendete Daten.
-
Jeder Zugriff auf gemeinsam verwendete Daten muss synchronisiert werden. Dazu verwendet man im einfachsten Fall eine "Mutex". Die einfachste, schnellste und auch ansonsten "billigste" Mutex unter Windows ist die CRITICAL_SECTION. Wenn du MFC verwendest kannst du die Klasse CCriticalSection verwenden, und zum Locken dann CSingleLock. Bevor du auf gemeinsam verwendete Daten zugreifst musst du (unabhängig davon aus welchem Thread der Zugriff erfolgt) die "Mutex" locken. Nachdem du fertig bist musst du sie wieder "freigeben" (unlock), damit auch andere Threads mal zugreifen können.
CSingleLock und ähnliche "scoped lock" Klassen automatisieren das "Freigeben" (unlock) indem sie es im Destruktor ausführen. Das ist praktisch weil man es so nicht vergessen kann, und es auch passiert wenn eine Excception geworfen wird. -
Wenn du Daten hast wo mehrere Werte "zusammengehören", musst du alle zusammengehörenden Werte "auf einmal" lesen/schreiben, also innerhalb ein und des selbem "lock/unlock" Paares der Mutex. Anders gesagt: wenn du "konsistente" Werte lesen willst darfst du die Mutex dazwischen nicht freigeben, und wenn du Daten schreibst musst du sicherstellen dass bei jedem "unlock" ein konsistentes Set an Werten in den gemeinsam verwendeten Daten steht.
Beispiel für solche Daten wären die Koordinaten für einen Punkt - X und Y gehören zusammen. Wenn sich der Thread der die Koordinaten schreibt oder der Thread welcher die Koordinaten liest nicht an diese Regel hält könnte es passieren dass der lesende Thread den X-Wert vom alten Punkt bekommt und den Y Wert vom neuen Punkt.
Ahja, wenn die Werte die zusammengehören über getrennte Mutexen synchronisiert werden sollen (oder müssen) wird es kompliziert
Ein Beispiel dafür wären Konten zwischen denen "Transaktionen" stattfinden sollen, also z.B. Geld von einem Konto auf's andere "verschieben". Am Einfachsten ist es wenn man solche Fälle ganz vermeidet, z.B. indem man alle Konten (oder was auch immer) über die selbe Mutex synchronisiert. Das kann in grossen Programmen auf grossen dicken Servern (mit vielen Cores/hardware Threads) natürlich zu Problemen führen, da diese einzige Mutex dann recht oft "gelockt" sein wird, und viele Threads u.U. recht lange warten müssen bis sie endlich ihren "lock" bekommen. Bekommt man damit ein Problem muss man sich eben was anderes (komplizierteres) einfallen lassen. -
Man sollte sich nicht in die Irre führen lassen und glauben dass man einzelne Werte (ein Integer, ein Zeiger, ...) lesen und/oder schreiben "darf" ohne eine Mutex zu locken. Das ist nämlich nicht so.
-
Das Keyword "volatile" sollte man am besten gleich garnicht kennen, und wenn man es kennt, gleich wieder vergessen. Sichtbarkeit von Änderungen sowie Synchronisierung von Zugriffen erzwingt/steuert man über Mutexen und sonst nix.

-
Man sollte sich von vornherein an sämtliche Regeln halten, und garnicht erst versuchen "auszuprobieren" was geht und was Probleme macht. Das dumme ist nämlich dass viele Dinge so aussehen als ob sie OK wären, obwohl sie es nicht sind. Bei vielen Fehlern die man bei Multithreading machen kann läuft alles im Test immer OK, auch wenn man den Test millionenmal wiederholt, bloss irgendwann (besonders schön wenn man das Programm schon ausgeliefert hat) zeigt der Fehler seine Wirkung, und dann hat man den Salat.
Im besten Fall stürzt das Programm dann einfach ab, im schlimmsten Fall liefert es ohne Hinweis einfach falsche Daten. Ganz toll wenn diese falschen Daten dann auch noch plausibel aussehen.
Andere Fehler wieder können durch anderen Code (der auch z.B. in einer ganz anderen Funktion stehen kann als der fehlerhafte Code) "maskiert" werden, so dass sie niemals eine Wirkung zeigen. Ändert man diesen "anderen" Code dann, wird der Fehler "aktiv" und man ist wieder angeschmiert.
----
Zusammengefasst: Multithreading mit "shared data" ist Die Hölle (tm), und verflixt schwer bis unmöglich zu testen/debuggen.
Für kleine private Projekte die nicht "kritisch" sind (=es passiert nix furchtbares wenn sie mal Mist bauen) ist das natürlich alles halb so wild. Allerdings kann es nie schaden es gleich "richtig" zu lernen, falls man später mal an "kritischeren" Projekten arbeitet in denen mehrere Threads eingesetzt werden.
p.S.: wenn man erstmal alles verstanden hat sind viele "einfachere" Programme mit Multithreading auch sehr einfach zu durchschauen. Das Dumme ist nur dass es (zumindest für mich) nahezu unmöglich ist mit ein paar einfachen Absätzen alles Nötige einem Anfänger zu erklären. Und das noch verständlich. Ein nicht ganz unpassender Vergleich wäre vielleicht die Grammatik einer Sprache wie Deutsch. Wenn man erstmal halbwegs gut Deutsch kann ist es einfach zu erkennen dass ein Satz wie "Ich bin ein Mensch." richtig ist, oder ein Satz wie "Du bin ein mensch." zumindest 2 Fehler enthält. Aber erklär mal jemandem der nicht Deutsch kann in ein paar Absätzen wie er selbst einfache deutsche Sätze zu formulieren hat, ohne dabei (Grammatik-)Fehler zu machen

-
-
So das hab ich jetzt:
im Header
typedef struct THREADSTRUCT { CAirScanDlg* _this; } THREADSTRUCT;im Quellcode
void CAirScanDlg::OnBnClickedScannen() { THREADSTRUCT* _param = new THREADSTRUCT; _param->_this = this; mChannel = 0; AfxBeginThread(ThreadFunc, _param, THREAD_PRIORITY_NORMAL, 0, 0); } UINT CAirScanDlg::ThreadFunc(LPVOID param) { THREADSTRUCT* pThis = (THREADSTRUCT*)param; // pThis->_this->UpdateData(TRUE); // GetDlgItem(IDC_SCANNEN)->EnableWindow(FALSE); // mStatusMsg = _T("Scanning... Please wait..."); UCHAR i, rssi = 0; int j,sum = 0; // pThis->_this->mChannel = 0; for(i=0; i<=NCHAN; i++) //Set number of channel for scanning { sum = 0; pThis->_this->SpiWriteData(pThis->_this->ioHandle, REG_CHANNEL, i); /* set channel */ pThis->_this->SpiWriteData(pThis->_this->ioHandle, REG_CONTROL, 0x80 + 0x10); /* set rx mode */ Sleep(2); //Sleep for > 150µs for settle channel ? pThis->_this->SpiReadData(pThis->_this->ioHandle, REG_RSSI); /*Read RSSI for checking valid ?*/ for(j=0; j<SCANLOOP; j++) //Set repeat for scanning single-channel { pThis->_this->SpiWriteData(pThis->_this->ioHandle, REG_CARRIER_DETECT, 0x80); /* turn on the carrier detect bit */ Sleep(2); /*Wait >50µs for getting channel-noise*/ rssi = pThis->_this->SpiReadData(pThis->_this->ioHandle, REG_RSSI); /*get byte from RSSI Register*/ pThis->_this->SpiWriteData(pThis->_this->ioHandle, REG_CARRIER_DETECT, 0x00);/* turn off the carrier detect bit */ sum += (rssi & 0x1F); /*Addup the recived values with RSSI-Bytes 4:0*/ pThis->_this->mSignalPuffer[i] = sum/SCANLOOP; //pThis->_this->OnPaint(); } pThis->_this->SpiWriteData(pThis->_this->ioHandle, REG_CONTROL, 0x00 + 0x10); /* clear rx mode */ //mSignalPuffer[i] = sum/SCANLOOP; /*Build a middlevalue from RSSI*/ pThis->_this->mChannel = i; pThis->_this->mProgress.SetPos(i); pThis->_this->OnPaint(); // UpdateData(FALSE); } // mStatusMsg = _T("Scan complete!"); // GetDlgItem(IDC_SCANNEN)->EnableWindow(TRUE); // mBool = TRUE; // OnPaint(); // pThis->_this->UpdateData(FALSE); //AfxEndThread(); return 0; }Ich hab jetzt noch keine zeit gehabt um mich mit Nutexe zu beschäftigen.
Aber bis jetzt schauts so aus, dass der Debugger sein OK gibt, aber bei der Ausführung nach jedem abgearbeitetens schritt der Schleife ein Assert-Fehler kommt. Sieht einer warum oder was ich vergessen/falsch gemacht habe?
-
Mann, dir muss man auch alles aus der Nase ziehen.
WELCHES ASSERT?
-
öhm...wenn das mal so einfach wäre. Kann nur das File angeben wo er meckert.
sp\vctool\vc7libs\ship\atlmfc\src\mfc\wincore.cpp
Line: 894Sprich an der stelle hier:
ASSERT((CWnd*)p == this); // must be us // Note: if either of the above asserts fire and you are // writing a multithreaded application, it is likely that // you have passed a C++ object from one thread to another // and have used that object in a way that was not intended. // (only simple inline wrapper functions should be used) // // In general, CWnd objects should be passed by HWND from // one thread to another. The receiving thread can wrap // the HWND with a CWnd object by using CWnd::FromHandle. // // It is dangerous to pass C++ objects from one thread to // another, unless the objects are designed to be used in // such a manner.#EDIT
Hab mal geschaut..scheint das OnPaint zu sein was ich aufrufe.
-
Warum rufst denn Du "OnPaint" auf???? Diese Methode sollte nur durch ein "WM_PAINT" aufgerufen werden!!!!
Verwende Invalidate...
-
Um ehrlich zu sein hab ich das net gewusst. Jetzt gehts, danke!
-
Oh Mann.
Du hast es gepostet, aber das Kommentar nicht gelesen, oder?Das passiert sogar bei uns in der Firma bei firmeninternem Code. Ich schreibe ein fettes ASSERT() irgendwo rein, und jmd. kommt zu mir weil "meine Library abstürzt". Sehe ich mir das "Problem" dann an, sehe ich ein ASSERT in meiner Library feuern, und das Kommentar was dabeisteht erklärt 1A genau das was der Aufrufer falsch gemacht hat.
Eieiei. Manche Leute sollten lernen etwas selbständiger zu sein.
Fragt euch einfach mal "was würde ich machen wenn ich keinen fragen könnte".
Erst wenn die eigenständige Suche schon zuviel Zeit in Anspruch genommen hat fragen, nicht vorher! (in der Firma würde ich sagen 15-60 Minuten, Zeit ist Geld, und mit Hilfe von irgendwem sind es oft nur 5-10 Minuten - *2 ist das immer noch weniger als mehrere Stunden selbst suchen -- zuhause würde ich sagen min. ein paar Stunden, denn einen Eintrag in ein Forum schreiben braucht auch Zeit, und man soll ja auch selber was dazulernen).scheint das OnPaint zu sein was ich aufrufe.
Da braucht garnix zu "scheinen", über den Callstack siehst du ja genau welche Funktion es ist, und wenn du die Zeile schon Quoten kannst, dann musst du ja nur gucken in welcher Funktion die drinnen steht... Eieiei
