Multithreading - _beginthread() - Problem
-
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