Hallo, ich hänge hier gerade an einer konzeptionellen Überlegung. Für eine Maschinensteuerung (Windows CE 5.0) soll ein Prozess (erstellt mit EVC++4.0) in mehrere Threads aufgeteilt werden. Der Prozess ist eine reine "Konsolenanwendung", d.h. hat keine Bedienoberfläche, sondern kommuniziert vie TCP mit einer anderen Anwendung. Nun möchte ich im groben folgendes realisieren: - Ein Thread kümmert sich um die TCP-Kommunikation, d.h. wartet auf eine Verbindung vom entfernten Programm und tauscht nach Verbindungsaufbau zyklisch Steuerdaten aus - Ein Thread realisiert eine/mehrere Schrittketten für die Ablaufsteuerung an der Maschine Somit existiert zu jeder "Funktionalität" nur genau ein Thread mit völlig unterschiedlicher Struktur. Die Threads sollen zur Ablaufkoordination lediglich beide Zugriff auf ein globales Struct haben. Nun meine Überlegungen: - Da die Boost-Libs meiner Recherche nach nicht unter CE laufen, würde ich die Threads mit den WIN32-C-Funktionen realisieren: http://msdn.microsoft.com/en-us/library/y6h8hye8.aspx - In die Struct, auf die beide Threads Zugriff erhalten sollen, würde ich ein CMutex einbauen und vor jedem Zugriff locken Meine Fragen: - Ist das prinzipiell ein brauchbares Konzept, oder ist da ein grober Denkfehler drin? - Sehe ich das richtig, dass ich für wirklich alle Funktionen, die ich aus BEIDEN Threads aufrufe, sicherstellen muss, dass diese Multithreading-Safe sind? Die C-Standard-Library und die WIN32-API sollten doch prinzipiell sicher sein, wenn ich _beginthread() an Stelle von CreateProcess() verwende? - Kann ich innerhalb jedes Threads weiterhin wie gewohnt mit new/delete Objekte erzeugen, ohne dass ich diese separat schützen muss (kein gemeinsamer Zugriff) - Muss ich die Threads noch irgendwie voreinander schützen? Ich würde mich freuen, wenn ich ein paar Kommentare zu obigen Zeilen bekommen könnte, um sicherzugehen, dass das Konzept halbwegs passt. Peter
> - Ist das prinzipiell ein brauchbares Konzept, oder ist da ein grober > Denkfehler drin? Das sollte so schon gehen. > - Sehe ich das richtig, dass ich für wirklich alle Funktionen, > die ich aus BEIDEN Threads aufrufe, sicherstellen muss, dass diese > Multithreading-Safe sind? Die C-Standard-Library und die WIN32-API > sollten doch prinzipiell sicher sein, wenn ich _beginthread() an > Stelle von CreateProcess() verwende? Du meinst vermutlich "an Stelle von CreateThread()". Aber auch das wird überbewertet. Sofern man nicht die ganz perversen C-Funktionen verwendet, die interne Statusvariablen nutzen (strtok ist wohl ein Kandidat), kann man auch ohne Probleme CreateThread verwenden. Ich mache das unter NT (2K/XP/2K3) seit 15 Jahren in auch ziemlich großen Systemen so, ohne auch nur ein einziges Mal ein _beginthread-Problem gehabt zu haben. > - Kann ich innerhalb jedes Threads weiterhin wie gewohnt mit > new/delete Objekte erzeugen, ohne dass ich diese separat > schützen muss (kein gemeinsamer Zugriff) Aber ja doch, sofern Du mit der geeigneten Multithreaded-Runtimelibrary linkst. - Muss ich die Threads noch irgendwie voreinander schützen? Nö, sofern Du Zugriffe auf gemeinsam genutzte Dinge mit "critical sections" oder anderen Synchronisationsobjekten schützt und ansonsten Threads mit Events o.ä. aufeinander warten lässt, ist alles wesentliche getan. Da der C-Compiler selbst nichts von Multithreading weiß, ist es sinnvoll, von mehreren Threads gemeinsam genutzte Variablen als volatile zu deklarieren, genau so, wie man es auch bei Nutzung von Interruptcode machen würde.
Hallo Rufus, vielen Dank für deine schnelle und verständliche Antwort! > > - Kann ich innerhalb jedes Threads weiterhin wie gewohnt mit > > new/delete Objekte erzeugen, ohne dass ich diese separat > > schützen muss (kein gemeinsamer Zugriff) > > Aber ja doch, sofern Du mit der geeigneten Multithreaded-Runtimelibrary > linkst. Das ist mir nicht ganz klar, new und delete gehören doch zum Sprachumfang von C++. Gegen welche Runtimelibrarys linke ich denn da? Ich vermute irgendwelche Systemaufrufe. Aber bei einem Multitaskingfühigen SDK sollte doch alles passen?! > Da der C-Compiler selbst nichts von Multithreading weiß, ist es > sinnvoll, von mehreren Threads gemeinsam genutzte Variablen als > volatile zu deklarieren, genau so, wie man es auch bei Nutzung von > Interruptcode machen würde. Das habe ich noch nirgends gelesen, danke für den Tipp! Peter
> Das ist mir nicht ganz klar, new und delete gehören doch zum > Sprachumfang von C++. Gegen welche Runtimelibrarys linke ich denn da? Na, die, in denen die Implementierung der C/C++-Standardfunktionen enthalten ist. Sieh Dir einfach mal den Inhalt des Library-Verzeichnisses Deines Compilers an, da wirst Du mehrere Libraries mit fast gleichem Namen finden. Bei VC6 für Win32 beispielsweise ist ein angehängtes "mt" der Hinweis auf die Multithreading-Variante, ein angehängtes "d" steht für die Debugversion etc. Das wird bei EVC++ nicht sehr anders aussehen. Du musst Dich glücklicherweise nicht damit auseinandersetzen, denn die IDE bietet Einstellmöglichkeiten, ob single- oder multithreading-Code erzeugt werden soll. Und das entscheidet dann unter anderem auch, welche Library verwendet wird.
Rufus t. Firefly wrote: > Du meinst vermutlich "an Stelle von CreateThread()". Aber auch das wird > überbewertet. Sofern man nicht die ganz perversen C-Funktionen > verwendet, die interne Statusvariablen nutzen (strtok ist wohl ein Man kann es meiner Meinung nach nicht laut genug sagen: Finger weg von CreateThread! Man muss schon ganz genau wissen was man macht, wenn man auf CreateThread setzt. Ich vermute mal, dass Du in Deinem "großen" Systen gegen die DLL-Version der CRT linkst. Hier ist es natürlich kein Problem, da die DLL eventuell fehlende per-thread-data in DllMain bei DLL_THREAD_ATTACH nachträglich allokieren kann und das bei Bedarf auch macht. Problematisch wird es aber in kleineren Progrämmchen, die statisch linken (und dann wird auch ein DisableThreadLibraryCalls zum Abenteuer). Und Peter wird ziemlich sicher statisch linken... Und so "pervers" müssen die Funktionen gar nicht sein. Schließlich ist auch alles das betroffen, was in irgendeiner Art und Weise locale-sensitive ist. Peter, bitte verwende auf alle Fälle _beginthreadex, denn damit funktioniert es immer. Alles andere ist viel zu unsicher.
> Ich vermute mal, dass Du in Deinem "großen" Systen > gegen die DLL-Version der CRT linkst. Nein, ich linke statisch. Interessieren würde mich ein Beleg, der tatsächlich zeigt, daß CreateThread Probleme verursacht, nicht nur Zitate von Warnungen in der Dokumentationen und allgemeinen Annahmen. Wäre ja schon interessant, zu sehen, warum es auch funktionieren kann. Wer hat eigentlich diese merkwürdige Formulierung "gegen etwas linken" erfunden?
Danke für eure weiteren Beiträge (und Bedenken)! > Du musst Dich glücklicherweise nicht damit auseinandersetzen, > denn die IDE bietet Einstellmöglichkeiten, ob single- oder > multithreading-Code erzeugt werden soll. Hm, hab grade mal sämtliche Compiler/Linkeroptionen in einem VS2008-Projekt überflogen, und keinen derartigen Schalter gesehen. Oder macht das der Compiler automatisch? > Und Peter wird ziemlich sicher statisch linken... Jein, sowohl statisch als auch DLLs die erst zur Laufzeit geladen werden (über getprocaddress()). Verstehe ich dich richtig, das das unkritisch sein sollte? Erstellt eine DLL für jeden Prozess oder sogar für jeden Thread einen eigenen Stack/Heap? Kann ich eine DLL überhaupt mehrfach (also für jeden Thread) innerhalb eines Prozesses laden? Oder muss ich die Funktionen global bereitstellen? Also wäre das ja ein Grund, gemeinsam genutzte Funktionen in eine DLL auszulagern, auch wenn das gar nicht nötig wäre. > Peter, bitte verwende auf alle Fälle _beginthreadex, denn damit > funktioniert es immer. Alles andere ist viel zu unsicher. Gern, spricht meiner bisherigen Recherche nach ja auch nichts dagegen > Wer hat eigentlich diese merkwürdige Formulierung "gegen etwas linken" > erfunden? Vermutlich ein Rechter mit Grammatikschwäche ;-) Peter
Rufus t. Firefly wrote: > Interessieren würde mich ein Beleg, der tatsächlich zeigt, daß > CreateThread Probleme verursacht, nicht nur Zitate von Warnungen in der Nun, intern werden nun mal auf den Thread bezogene Daten gehalten. Diese Daten müssen irgendwo gespeichert werden. Rufst Du eine perverse Funktion auf, z.B. isspace (locale-sensitive), wird der Platz für diese Daten allokiert, so es nicht bereits geschehen ist. Soweit ist das also kein Problem. Wenn Du nun den Thread in Deiner statischen Variante enden lässt, ist da niemand, der den Platz für diese Daten wieder freigibt. Solche Lecks sind in meinen Augen schon ein Problem, unabhängig der Aussagen der Marketings. Nimmst Du hingegen _beginthreadex, passiert das Freigeben automatisch: Kein Leck, kein Problem (solange man die Finger von der ebenfalls "verbotenen" Funktion ExitThread lässt).
Peter wrote: >> Du musst Dich glücklicherweise nicht damit auseinandersetzen, >> denn die IDE bietet Einstellmöglichkeiten, ob single- oder >> multithreading-Code erzeugt werden soll. > Hm, hab grade mal sämtliche Compiler/Linkeroptionen in einem > VS2008-Projekt überflogen, und keinen derartigen Schalter gesehen. Oder > macht das der Compiler automatisch? Rufus meint das, was Du unter Konfigurationseigenschaften -> C++ -> Codegenerierung -> Laufzeitbiliothek findest. Und nein, hier man man nicht (mehr) auf single-threaded umstellen. Das war zu Zeiten des VC6 noch anders. Mit Deinem VS2008 hingegen bist Du hier immer auf der sicheren Seite. > Jein, sowohl statisch als auch DLLs die erst zur Laufzeit geladen werden > (über getprocaddress()). Verstehe ich dich richtig, das das unkritisch > sein sollte? Erstellt eine DLL für jeden Prozess oder sogar für jeden > Thread einen eigenen Stack/Heap? Kann ich eine DLL überhaupt mehrfach Der Stack liegt in der Hand des Threads. Das ist also immer der selbe, egal ob DLL oder EXE. Beim Heap sieht das aber anders aus: - Linkst Du ein Modul (EXE oder DLL) gegen die statische Version der CRT, benutzt diese Modul auch einen eigenen Heap. Das free muss demnach dort passieren, wo das malloc passiert ist. Modul A kann den von Modul B angeforderten Speicher nicht freigeben. - Linkst Du gegen die DLL-Version der CRT, passiert das malloc und free automatisch an der richtigen Stelle. Hier kannst Du Speicher von Modul A in Modul B freigeben. - Sind Modul A und Modul B gegen die DLL-Version der CRT gelinkt, dies aber in unterschiedlichen Versionen (z.B. EXE mit dem VS2008, DLL noch auf dem Stand VC6), musst Du mit den Freigaben natürlich wieder aufpassen. Am Besten ist es aber, sich auf nichts und niemanden zu verlassen. Das Modul, das Speicher anfordert, gibt diesen wieder frei, niemand anders. > (also für jeden Thread) innerhalb eines Prozesses laden? Oder muss ich > die Funktionen global bereitstellen? > Also wäre das ja ein Grund, gemeinsam genutzte Funktionen in eine DLL > auszulagern, auch wenn das gar nicht nötig wäre. Eine DLL wird nicht in einen Thread geladen, sondern in den Adressraum des Prozesses. Wenn Du die DLL erst einmal geladen hast, können alle Threads des Prozesses auf deren Funktionen nach belieben eindreschen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.