Multithreading - _beginthread() - Problem
-
Sehr schöner Multithreading-Fehler

Du kopierst die Zahl erst im neuen Thread! Wann die neuen Threads starten, ist meistens nicht definiert. Es kann sein, dass der Main-Thread zuerst alle Threads startet, wodurch alle Threads später dann 5 ausgeben, da sie alle auf die gleiche Speicheradresse zugreifen.
Du musst also jedem Thread den eigenen Speicher übergeben. Das kann man zum Beispiel sehr schön mit Klassen erreichen. Oder auf die einfache Art, wie es LordJaxom gezeigt hat.
Ich empfehle dir allerdings zuerst zwei andere Sachen:
1. Wirf VC6 in den Mülleimer und nimm lieber die VC8 Express Edition (gratis).
2. Nimm lieber Boost.Thread für die Threaderstellung. Deutlich bessere API, flexibler und Platformunabhängig. ( http://www.boost.org/ )Grüssli
-
// Soll warten bis sich alle 5 Threads korrekt angemeldet haben while (threadsstarted < 5) {} // Hauptschleife während des Threadbetriebs do { WaitForSingleObject( MutexThread, INFINITE ); cnt++; printf("Thread %i: %i\n", MyID, cnt); ReleaseMutex( MutexThread ); } while ( WaitForSingleObject( MutexGlobal, 0 ) == WAIT_TIMEOUT );Lass mich mal versuchen... als thread moderator
Also Thread 5 wird gestartet erhöht threadsstarted und läuft über die while Schleife einfach drüber. Ist immer noch 'am Ball'.
Erhält jetzt die Mutex und gibt seinen Text aus. Er hat immer noch den Ball verliert den Mutext (im release) aber hat den Ball noch (läuft noch) jetzt holt er sich die Mutext gleich wieder und gibt nochmal seinen text aus .....Ich weiß nicht wie es in Windows genau implementiert ist, aber normalerweise erhält der Thread der einen 'freien' mutext haben will ihn auch und läuft einfach weiter.
-
Danke euch erstmal für eure Beiträge

Das mit dem Array hatte ich mir auch schon so ähnlich überlegt. So werd ich das mal probieren.
Leider bin ich an VC++ 6.0 und an die "_beginthread"-geschichte durch Unternehmensvorgaben gebunden
Weiß auch nicht warum wir eine 10 Jahre alte Entwicklungsumgebung verwenden müssen.
-
Die Art und weise wie du auf threadsstarted zugreifst ist auch falsch.
Falsch falsch falsch.Und Mutexen sind hier zwar korrekt, aber Overkill. Du kannst hier CRITICAL_SECTIONs anstelle der Mutexen verwenden.
-
Inwiefern ist die Art falsch? Ist es grob gefährlich oder einfach nur Laufzeit-ineffizient?
-
Erstens ist der Zugriff nicht synchronisiert.
Zweitens ist Aktives Warten in einer Multitask-Umgebung böse.
-
Zur weiteren Übung habe ich mal ein Programm geschrieben.
Es besteht aus der Main (Thread 0) sowie 3 weiteren Worker-Threads.
Die Main dient in dem Fall nur als Generator für Zeitliche Impulse (Ticks).
In diesem Fall wird alle 700 Clockticks ein Ticksignal gesendet und alle 1000 Clockticks ein zweites.Worker 1 wartet auf Tick1 (700) um dann eine zufällige Zahl zu erzeugen (verkörpert eine Datenbank die hinundwieder mal neue Daten erhält).
Worker 2 wartet auf Tick2 (1000) um dann eine zufällige Zahl zu erzeugen (verkörpert eine Datenbank die hinundwieder mal neue Daten erhält).
Worker 3 wartet auf neue Daten bei Worker 1 & 2 um diese dann einzusammeln und an einen Vektor anzuhängen.
Sobald sowohl von Worker 1 als auch von Worker 2 mindestens 10 Integers aufgelaufen sind, wird abgebrochen.
(Worker 3 symbolisiert eine Verarbeitungsroutine für die Daten von Worker 1 & 2)#include "stdafx.h" #include <process.h> #include <windows.h> #include <stdlib.h> #include <stdio.h> #include <time.h> #include <vector> using namespace std; HANDLE MutexGlobal; HANDLE MutexThread; clock_t GlobalClock; bool Tick1, Tick2, NewData1, NewData2; bool finished = false; int Data1, Data2; void WorkerThread1( void *pMyID ) { int MyID = *(int*)pMyID; // Thread meldet Bereitschaft printf("WorkerThread ready to Work\n", MyID); // Hauptschleife während des Threadbetriebs do { WaitForSingleObject( MutexThread, INFINITE ); if (Tick1) { Data1 = rand() % 10 + 1; printf("[%i] Tick1 found\n", clock()-GlobalClock ); Tick1 = false; NewData1 = true; } ReleaseMutex( MutexThread ); } while ( WaitForSingleObject( MutexGlobal, 0 ) == WAIT_TIMEOUT ); } void WorkerThread2( void *pMyID ) { int MyID = *(int*)pMyID; // Thread meldet Bereitschaft printf("WorkerThread ready to Work\n", MyID); // Hauptschleife während des Threadbetriebs do { WaitForSingleObject( MutexThread, INFINITE ); if (Tick2) { Data2 = rand() % 10 + 1; printf("[%i] Tick2 found\n", clock()-GlobalClock ); Tick2 = false; NewData2 = true; } ReleaseMutex( MutexThread ); } while ( WaitForSingleObject( MutexGlobal, 0 ) == WAIT_TIMEOUT ); } void WorkerThread3( void *pMyID ) { int MyID = *(int*)pMyID; // Thread meldet Bereitschaft printf("WorkerThread ready to Work\n", MyID); // Hauptschleife während des Threadbetriebs vector <int> Daten1; vector <int> Daten2; do { WaitForSingleObject( MutexThread, INFINITE ); // Überprüft ob Worker1 neue Daten hat if (NewData1) { Daten1.push_back( Data1 ); printf("[%i] Data1 found (%i)\n", clock()-GlobalClock, Daten1.size() ); NewData1 = false; } // Überprüft ob Worker2 neue Daten hat if (NewData2) { Daten2.push_back( Data2 ); printf("[%i] Data2 found (%i)\n", clock()-GlobalClock, Daten2.size() ); NewData2 = false; } // Abbruchbedingung (wenn von beiden Workern 10 Integers aufgelaufen sind ) if ( (Daten1.size() >= 10) && (Daten2.size() >= 10) ) finished = true; ReleaseMutex( MutexThread ); } while ( WaitForSingleObject( MutexGlobal, 0 ) == WAIT_TIMEOUT ); } int main(int argc, char* argv[]) { Tick1 = false; Tick2 = false; NewData1 = false; NewData2 = false; srand( NULL ); // Mutex erzeugen MutexGlobal = CreateMutex( NULL, TRUE, NULL ); MutexThread = CreateMutex( NULL, FALSE, NULL ); int ThreadNr = 1; _beginthread( WorkerThread1, 0, &ThreadNr ); ThreadNr++; _beginthread( WorkerThread2, 0, &ThreadNr ); ThreadNr++; _beginthread( WorkerThread3, 0, &ThreadNr ); // Tick-generator clock_t Clock1; clock_t Clock2; Clock1 = clock(); Clock2 = clock(); GlobalClock = clock(); while (!finished) { if (clock() - Clock1 > 700) { Tick1 = true; Clock1 = clock(); printf("[%i] Tick1\n", clock()-GlobalClock ); } if (clock() - Clock2 > 1000) { Tick2 = true; Clock2 = clock(); printf("[%i] Tick2\n", clock()-GlobalClock ); } } // Thread beenden, Mutexes löschen ReleaseMutex( MutexGlobal ); ReleaseMutex( MutexGlobal ); ReleaseMutex( MutexGlobal ); CloseHandle( MutexThread ); CloseHandle( MutexGlobal ); return 0; }nun zur Frage:
Wie findet ihr die Realisierung?
Was kann ich noch besser machen? Was geht effizienter? Was ist scheisse?

-
It0101 schrieb:
Die Main dient in dem Fall nur als Generator für Zeitliche Impulse (Ticks).
In diesem Fall wird alle 700 Clockticks ein Ticksignal gesendet und alle 1000 Clockticks ein zweites.Du wartest immer noch aktiv auf diese "Ticksignale" und auf die Clock. Schau dir mal Sleep und Events an.
-
D.h. wenn ich dich richtig verstanden habe kann ich mit "Sleep(...)" einen Thread schlafen legen, wenn ich die Länge des Zeitintervals kenne, wo ich ihn nicht brauchen werde?
Würde in meinem Fall ja zutreffen, da ich dann Worker 1 z.b. nach Erzeugen der Daten für einige Millisekunden schlafen legen könnte. Gleiches bei Worker 2.
Mal gucken was die Events bieten

-
It0101 schrieb:
Würde in meinem Fall ja zutreffen, da ich dann Worker 1 z.b. nach Erzeugen der Daten für einige Millisekunden schlafen legen könnte. Gleiches bei Worker 2.
Du sollst in deinem Hauptthread Sleep benutzen, damit du nicht sinnlos die Clock pollst. Das verbrät unnütz Prozessorleistung. Und die die Wokerthreads sollten auf Events warten, die im Hauptthread gesetzt werden.
-
aso ok, so meintest du das

Bin grad dabei mich mit der "CEvent" Klasse zu beschäftigen. Sieht aber nach ner MFC geschichte aus.
Scheint aber prinzipiel ähnlich zu funktionieren wie die _beginthread Geschichte.
Mal gucken ob Events auch ohne MFC machbar sind.
-
It0101 schrieb:
Bin grad dabei mich mit der "CEvent" Klasse zu beschäftigen. Sieht aber nach ner MFC geschichte aus.
Das gibt's auch ohne MFC: CreateEvent.
-
ah ok
daher weht der Wind.Habe nämlich gerade versucht die CEvent zu kriegen, durch einbinden von "afxmt.h" was wiederum zum rauswerfen der "windows.h" führen musste und was wiederum weitaus mehr fehler verursachte

-
ok läuft jetzt mit den Events schon viel besser. Schon ein kleines Sleep (Bsp. 10ms) bringt schon deutliche Vorteile gegenüber vorher. Kaum noch CPU-Belastung und die Events sind eine ziemlich elegante Lösung der ganzen Sache.
-
#include <stdlib.h> #include <stdio.h> #include <time.h>falsch... ist alles deprecated...
//global HANDLE MutexGlobal; HANDLE MutexThread; clock_t GlobalClock; bool Tick1, Tick2, NewData1, NewData2; bool finished = false; int Data1, Data2;normalerweise macht man das nicht global sondern überigbt es halt als struct an den thread...
int MyID = *(int*)pMyID;wie wärs mit
int MyID = * (static_cast <int*> (pMyID) );printf("WorkerThread ready to Work\n", MyID);Wie wärs da mit:
std::cout << "WorkerThread #" << MyID << " ready to Work << std::endl;wobei man in threads normalerweise keine ausgabe haben sollte... postmsg und so wären da die api-fkt
Manche IDEs bieten aber an, cout threadsafe zu machen ^^außerdem würd ich IMMER _beginthreadex (...) nehmen (auch, wenn es nicht von nöten ist - irgendwann will man dann doch ma das handle haben und vll drauf warten oder so... und _beginthread (...) garantiert dir nicht, dieses zu bekommen...)
und würd dir auch empfehlen noch ne wrapper-(template-)klasse für threads zu schreiben - so viel arbeit sollte das jz nich sein aber dir einiges an arbeit ersparen ^^
bb : )
-
Vielen Dank

Da waren ja reichlich gute Vorschläge dabei. Die globalen Variablen haben mich eh schon genervt. Das mit dem struct ist eine gute Idee

-
Hatte soeben einen Fall von "nicht-Threadsave" bei "cout".
In diesem Fall waren alle Berechnungen richtig, nur eben die Ausgabe totaler Schrott.
Woran liegt das, dass dann manchmal totaler Müll ausgegeben wird?
Kann man das verhindern, indem man das "cout" rundherum absichert?
So z.B. :WaitForSingleObject( Mutex, INFINITE ); cout << "Foo" << endl; ReleaseMutex( Mutex );Gibt es irgendwo Listen von Befehlen die auch nicht "threadsave" sind?
-
so weit ich weiß, gibts bei MSVC ne einstellung für cout, was es threadsafe macht... is aber kein standard
außerdem sollte man in threads keine ausgabe machen....
aber iwo gibs hier im forum so ne klasse - das thema hatte ich auch mal..hier:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-204899-and-start-is-30.htmlbb