Samstag, 16. Februar 2008

Ordnungsaspekte für das Multithreading [OOP 2008]

Auf der VSone 2008 habe ich gerade einen Vortrag über Software Transactional Memory (STM) gehalten und meine Open Source Implementation NSTM vorgestellt. Der Vortrag war gut besucht; das hat mich sehr gefreut und ermutigt, weiter an NSTM zu arbeiten. Ein wenig Hilfe von anderen Entwicklern wäre dabei natürlich schön... Bei den Collections, die für einen STM neu entwickelt werden müssen, ist einiges zu tun. (Vom .NET Fx angebotene Collections wie List<T> oder Queue<T> können nicht einfach "von außen" transaktional gemacht werden, da ihre internen Strukturen nicht auf STM basieren. Nur eine grundsätzliche thread-safety lässt sich von außen durch pauschale Sperren herstellen.)

Nun finde ich Ordnung/Überblick immer gut. Deshalb habe ich mich gefreut, beim Blick auf die VSone Agenda eine persönliche Erkenntnis gehabt zu haben. Da war nämlich nicht nur ich mit einem Multithreading-Vortrag vertreten, sondern auch Bernd Marquardt zum Thema OpenMP. Dazu kommt noch, dass ich neulich ein wenig über Microsofts Concurrency Coordination Runtime (CCR) gelesen hatte.

Meine Erkenntnis war nun, dass ich in diesen/zwischen diesen Technologien ein Muster erkannt habe. Und Muster als Strukturierungen der Realität finde ich immer gut. Die erzeugen in mir immer ein Gefühl von Entspannung und Aha nach dem Motto "Achsoooo, so ist das! Eigentlich ganz einfach..." :-)

Das Muster, das ich nun gesehen habe, betrifft die Einteilung von Multithreading-Themen. Herb Sutter hat ja auch schonmal eine Einteilung in "The Pillars of Concurrency" gemacht. Die gliedert die Parallelverarbeitung nach Zwecken. Das ist aber nur eine mögliche Dimension, finde ich. Eine andere ist die Einteilung nach "technischen Aspekten".

Für mich teilt sich Multithreading danach auf in drei grundsätzliche Problembereiche:

  1. Code auf Threads verteilen: Code in einem eigenen Thread laufen zu lassen, kann so einfach sein, wie ThreadPool.QueueUserWorkItem() aufzurufen. Zum ersten Aspekt des Multithreading gehört aber mehr. Denn Code explizit auf Threads zu verteilen ist letztlich umständlich oder gar eine intellektuelle Herausforderung. Viel effizienter wäre es doch, wenn Sie sich über die Parallelisierung von Code gar keine Gedanken machen müssten. Mit implizitem oder zumindest deklarativem Multithreading wäre Ihnen eine Last genommen. OpenMP oder Active C# sind dafür hilfreiche Technologien. Mit ihnen erzeugen Sie Threads nicht mehr explizit im Code, sondern sagen viel pauschaler "Hier soll etwas automatisch parallel laufen. Irgendwie."
  2. Parallelen Code koordinieren: Paralleler Code läuft selten isoliert. Er muss vielmehr in seinen Aktivitäten koordiniert werden. Threads müssen sich abstimmen, in dem was sie tun. Sie wollen einander Aufforderungen schicken oder aufeinander warten. Der .NET Fx bietet dafür z.B. WaitHandle verschiedener Art. Aber andere Technologien machen es noch einfacher, z.B. die Ports der CCr oder Protokolle in Active C#. Ganz allgemein geht es bei der Koordinierung immer um Signale in Warteschlangen, auf die man warten kann.
  3. Gemeinsame Ressourcen parallel nutzen: Wenn Threads dann laufen und sich koordinieren, womit arbeiten Sie dann? Häufig auf gemeinsamen Ressourcen. Die können sie aber nicht einfach so ohne Beachtung der anderen Threads egoistisch nutzen! Parallele Nutzung gemeinsamer Ressourcen läuft immer Gefahr, Inkonsistenzen zu erzeugen  - wenn sie sich nicht koordiniert. Koordinationstechnologien helfen also bei der gemeinsamen Ressourcenutzung. Aber eigentlich ist explizite Koordination genauso ineffizient wie explizite Parallelisierung. Auch hier ist also Abhilfe gefordert. Software Transactional Memory oder auch Space Based Collaboration sind Ansätze, die die Parallelnutzung leichter machen. Sie bieten Abstraktionen, hinter denen konkurrierende Zugriffe und ihre explizite Koordination verschwinden.

Wenn ich zukünftig irgendwo etwas über Multithreading lese, dann werde ich mich sofort fragen, um welchen dieser drei Aspekte es dabei geht. Irgendwie ordnet sich für mich dadurch das Thema besser als bisher.

Und ich habe damit Oberbegriffe in der Hand, die ich auch auf Herb Sutters Zwecke anwenden kann. Wie ist´s z.B. mit der Skalierbarkeit? Welche Aspekte haben Einfluss auf sie? Größere Skalierbarkeit ergibt sich z.B. immer durch weniger Koordination und weniger gemeinsame Ressourcen. Oder wie ist´s mit Responsiveness? Dafür ist es vor allem wichtig, Code einfach parallelisieren zu können. Dann nutze ich Parallelität nämlich eher, um responsive zu bleiben.

Würde mich freuen, wenn diese Aspekte Ihnen auch ein wenig helfen würden, die Multithreading-Welt "geordneter" zu sehen.

3 Kommentare:

Maik G. Seewald hat gesagt…

Hallo Frank, leider arbeitet OpenMP nicht ganz so easy, was die Verteilung der Threads betrifft. Ich muss schon sehr genau wissen, was ich da wie verteile und wie ich Informationen zwischen den Threads austausche. Sicherlich automatisiert OpenMP vieles, aber ich kann immer noch Dead-Locks produzieren oder in Race-Conditions laufen. Aus meiner Sicht sind die Angebote der Softwareindustrie (IDE, Compiler), die Aufgaben der Parallelisierung effektiv zu lösen, nicht ausreichend. Auch die Funktionalen Programmiersprachen stecken noch in den Kinderschuhen. Ich schreibe übrigens zu diesem Thema in losen Abständen in/auf meinem Blog:
http://webduke.blogspot.com/
Viele Grüsse, Maik

Ralf Westphal - One Man Think Tank hat gesagt…

@Maik: Ich weiß nicht, ob du mich meinst, denn ich bin Ralf und nicht Frank ;-) Aber ich nehme es mal an.

Natürlich muss ich mich auch mit OpenMP um Deadlockvermeidung kümmern. Nichts anderes habe ich behauptet. Deshalb steht OpenMP ja beim ersten Aspekt des Multithreadings und nicht beim dritten.

Maik G. Seewald hat gesagt…

Sorry Ralf. Frank lief gerade in einem anderen Thraed und der war nicht richtig synchronisiert ;-)

Kommentar veröffentlichen

Hinweis: Nur ein Mitglied dieses Blogs kann Kommentare posten.