Forum: PC-Programmierung Multithreading unter WinCE


von Peter (Gast)


Lesenswert?

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

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> - 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.

von Peter (Gast)


Lesenswert?

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

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> 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.

von René K. (king)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> 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?

von Peter (Gast)


Lesenswert?

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

von René K. (king)


Lesenswert?

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).

von René K. (king)


Lesenswert?

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
Noch kein Account? Hier anmelden.