Datenaustausch über Listen mit mehreren Threads
-
Hallo C++ Experten,
brauche bitte mal etwas Unterstützung bei meinem Vorhaben...
Ich habe eine Serveranwendung geschrieben, die mehrere Clients parallel behandelt.
Für jeden neuen Client der sich mit meinem Server verbindet, mache ich einen neuen Thread auf (erzeuge ein neues Threadobjekt meiner Threadklasse). Die Clients können nun beliebig viele Jobanfragen an den Server stellen die ich in einer stl list<T> eintrage.Das ganze funktioniert soweit ganz gut, aber ...
Ich möchte das jeder Thread seine Anfragen in die list schreibt und ein weiterer Thread sich auf der anderen Seite um das herrausnehmen der Jobanfragen kümmert und diese dann z.B. zur Verarbeitung an einen A/D-Wandler weitergibt.
Meine erste Frage hierzu
1. Ist es sinnvoller für jeden Client (Thread) eine eigene Liste zu haben und einen weiteren eigenen Thread für die Messaufträge, oder sollten lieber alle Threads in eine gemeinsame Liste schreiben bzw. dann auch mehrere aus einer gemeinsamen Liste entnehmen? (zB. Synchronisation über Semaphore).
2. Ich schaffe es nicht Daten, die ich an einem Ende in die Liste schreibe, mit dem Messthread wieder herauszunehmen. Da ich ja die Threads über Objekte realisiert habe, hat natürlich jeder Thread seine eigenen Eigenschaften bzw seine eigenen list-Objekte(von der stl list). Gibt es eine Möglich mit dem Messthread auf die selbe Liste zuzugreifen in die der Clientthread schreibt. (Mit static hats irgenwie nicht funktioniert)

Die Liste befindet sich in einer separaten Klasse "class MyList"danke für eure Zeit
-
@1: Ob es sinnvoll ist dass jeder "Connection-Thread" seine eigene Liste hat weiss ich nicht (ich kenne deine Anwendung nicht), ich schätze aber eher nicht. Synchronisieren über eine Mutex (bitte schick keine Semaphore um den Job einer Mutex zu machen) musst du so oder so, das bleibt dir nicht aus.
Aber bitte beschreibe mal wie die Kommunikation zwischen Client und Server aussehen sollte. Also wer sendet wann was und wie reagiert der andere darauf. Ich weiss im Moment nicht genau wie du dir das vorstellst, also half-duplex oder full-duplex und wenn half-duplex ob dann die Antworten erst gesendet werden sollen wenn alle Anfragen empfangen wurden oder immer eine Antwort sofort nach jeder Anfrage etc.
@2: Wenn du nur wissen willst wie die Connection-Threads und der Worker-Thread auf die gleiche Liste zugreifen können, das ist relativ einfach:
class WorkItemQueue { ... }; // die "Anfragen-Liste" class ClientConnection { public: ClientConnection(SOCKET socket, WorkItemQueue* queue); // ... }; class WorkerThread { public: WorkerThread(WorkItemQueue* queue); // ... }; class Server { public: void start() { // ... m_worker = new WorkerThread(&m_queue); // ... } // ... void accept() { SOCKET s = 0; try { s = ::accept(...); ClientConnection* cc = new ClientConnection(s, &m_queue); // ... } catch (...) { if (s) ::closesocket(s); } } // ... private: // ... WorkItemQueue m_queue; WorkerThread* m_worker; };Der Code ist bitte nur grob skizziert.
Real solltest du RAII für den Socket und Smart-Pointer für alles andere verwenden, erleichtert das Leben ungemein. Falls du nicht weisst was das ist schlag RAII nach (z.B. Wikipedia oder einfach mal im Google eingeben), und was Smart-Pointer angeht: guck dir boost::shared_ptr an, das ist quasi DER Standard für Smart-Pointer.
-
Die Adresse auf die queue dem Konstruktor mit zugeben ist natürlich naheliegend und funktioniert auch sehr gut. Danke für den Tipp.
Du hast allerdings recht das ich in jedem Falle eine Synchronisation brauche (hier meinte ich einen binären Semaphor).
Es ging mir an dieser Stelle eher darum das ein ClientThread in eine (oder nur in seine) Liste schreibt während einer weiterer WorkerThread aus dieser bzw. nur aus seiner Liste entnehmen soll. Soweit sogut...
Die hintergedanke an dieser Stelle war eher, das der WorkerThread diese Clientanfragen aus der Liste nimmt und sie an einen A/D-Wandler zur Weiterbearbeitung überträgt...
Jetzt hatte ich mir überlegt, da der A/D-Wandler ja immer nur einen Auftrag zu einem Zeitpunkt ausführen kann und ein Auftrag ruhe auch mal etwas länger dauern könnte, ob es passieren kann bei einer globalen Listen und einem einzigen WorkerThread, das dieser dann auch etwas länger blockiert wird???
Das mit der Kommunikation zwischen Client und Server ist an dieser Stelle nicht so wichtig, aber da du gefragt hast...
Ich verarbeite die Clienteingaben in einem StringStream und übergebe sie einer Protokollklasse zur Syntaxüberprüfung. Nur bei korrekter Syntax (z.B. start int1 int2) werden neue Listenknoten erzeugt, die bestimmte Inhalte haben und Zeiger auf die Listenknoten in die Liste eingetragen...
-
hier meinte ich einen binären Semaphor
Mit ist klar dass du ne "binary semaphore" meintest, bloss ist das auch nicht das richtige Werkzeug hier. Ne "binary semaphore" ist "unowned", ne Mutex dagegen ist "owned", ansonsten unterscheiden die sich nicht. Und für die Synchronisierung von Zugriffen auf gesharte Resourcen nimmt man eben Mutexen weil man eben "owned" will und nicht "unowned".
Jetzt hatte ich mir überlegt, da der A/D-Wandler ja immer nur einen Auftrag zu einem Zeitpunkt ausführen kann und ein Auftrag ruhe auch mal etwas länger dauern könnte, ob es passieren kann bei einer globalen Listen und einem einzigen WorkerThread, das dieser dann auch etwas länger blockiert wird???
Natürlich kann das passieren, aber wie willst du das verhindern?
Mehrere Listen helfen da auch nicht.
Der Flaschenhals ist in dem Fall der Worker-Thread (bzw. der AD Wandler), da kannst du nix dran ändern indem du mehrere Listen verwendest.
Und die Liste wird ja nicht blockiert, der Worker-Thread muss die Mutex die die Liste schützt ja nur gelockt halten während er die Anfrage aus der Liste rausnimmt und später wenn er die Antwort reintut, nicht aber während er die Anfrage ausführt.
-
Ich hatte mir das so gedacht, dass ich eine weitere Liste nehme (und hier nur eine) wo alle fertigen (bearbeiteten) Aufträge drinstehen, da diese ja nur z.B. einen analogen Wert haben und nicht mehr wie in der Jobliste Zeiger auf eine Klasse (Listenknoten) sind.
Würde jetzt der WorkerThread durch einen längeren Auftrag blockiert werden kann in der Zeit kein anderer Client seine fertigen Aufträge aus der fertig-Liste bekommen, da der WorkerThread diese auch wieder abholt. Wenn aber für jeden Client ein WorkerThread die Arbeit macht und einer wäre blockiert, könnten die anderen wenigstens ihre Aufträge abholen!?
Oder ich lasse die ClientThreads die Aufträge aus der fertig-Liste holen?

-
nur so aus interesse ... Muss es denn immer ein neuer Thread für jeden Clienten sein ? Arbeite doch alle Clienten mit "select()" ab ?! dann wärs auch portabel (weitgehend)
Gruß Chris