Die Kunst eines guten Designs ist, die Methode zu wählen, mit denen die Anforderungen am besten erfüllt werden. Die
Kunst eines guten parallelen Systemdesigns ist häufig die, die Methode zu wählen, mit denen die
Parallelitätsanforderungen am einfachsten erfüllt werden. Eine der ersten Regeln für Designer ist zu vermeiden, das
"Rad neu zu erfinden". Für die Lösung der meisten Probleme wurden bereits taugliche Designmuster und Designidiome
entwickelt. In Anbetracht der Komplexität paralleler System ist es sinnvoll, bewährte Lösungen zu verwenden und das
Design so einfach wie möglich zu gestalten.
Parallele Aufgaben, die vollständig innerhalb eines Computers ausgeführt werden können, werden
Ausführungs-Threads genannt. Wie alle parallelen Aufgaben sind Ausführungs-Threads ein abstraktes Konzept, da
sie in zeitlicher Abfolge ausgeführt werden. Die einzige Möglichkeit, einen Ausführungs-Thread physisch zu erfassen,
besteht darin, seinen Zustand zu einem bestimmten Zeitpunkt darzustellen.
Der direkteste Weg für die Darstellung paralleler Aufgaben mit Computern ist, jeder Aufgabe einen gesonderten Computer
zuzuordnen. Dies ist gewöhnlich jedoch zu kostenintensiv und trägt nicht immer zur Konfliktlösung bei. Deshalb werden
im Allgemeinen mehrere Aufgaben durch irgendeine Form von Multitasking in demselben physischen Prozessor
unterstützt. In diesem Fall werden der Prozessor und die zugehörigen Ressourcen wie Hauptspeicher und Busse gemeinsam
genutzt. (Leider kann diese gemeinsame Nutzung von Ressourcen zu neuen Konflikten führen, die beim ursprünglichen
Problem nicht vorlagen.)
Die gebräuchlichste Form des Multitasking ist die Bereitstellung eines "virtuellen" Prozessors für jede Aufgabe. Dieser
virtuelle Prozessor wird normalerweise als Prozess oder Task bezeichnet. Normalerweise hat jeder Prozess einen
eigenen Adressraum, der sich logisch vom Adressraum anderer virtueller Prozessoren unterscheidet. Dadurch wird
verhindert, dass miteinander in Konflikt stehende Prozesse versehentlich den Hauptspeicher des jeweils anderen
überschreiben. Leider ist der Aufwand des physischen Prozessors für jeden Prozesswechsel häufig unverhältnismäßig hoch.
Ein Prozesswechsel bedeutet die Auslagerung von Registersets in der CPU (Kontextwechsel), die selbst mit
modernen Hochgeschwindigkeitsprozessoren mehrere Hundert Mikrosekunde dauern kann.
Zur Verringerung dieses Aufwands bieten viele Betriebssysteme die Möglichkeit, mehrere Lightweight-Threads in
einem einzelnen Prozess auszuführen. Die Threads in einem Prozess verwenden denselben Adressraum wie der Prozess. Dies
verringert den Aufwand für den Kontextwechsel, erhöht aber die Wahrscheinlichkeit von Speicherkonflikten.
Für einige durchsatzstarke Anwendungen kann selbst der Aufwand für Lightweight-Thread-Switching unangemessen hoch sein.
In solchen Situationen wird gewöhnlich eine noch schlankere Form des Multitasking verwendet, bei der einige spezielle
Features der Anwendung genutzt werden.
Die Parallelitätsanforderungen des Systems können dramatische Auswirkungen auf die Systemarchitektur haben. Die
Entscheidung, Funktionalität von einer Einzelprozessarchitektur auf eine Mehrprozessarchitektur umzustellen, kann
erhebliche Änderungen an der Systemstruktur mit sich bringen und das in vielerlei Hinsicht. Es müssen möglicherweise
zusätzliche Mechanismen (z. B. Fernprozeduraufrufe) eingeführt werden, die die Architektur des Systems wesentlich
verändern können.
Es müssen Anforderungen an die Systemverfügbarkeit sowie der zusätzliche Aufwand für die Verwaltung zusätzlicher
Prozesse und Threads berücksichtigt werden.
Wie bei den meisten Architekturentscheidungen werden beim Ändern der Prozessarchitektur einige Probleme gegen andere
eingetauscht:
Ansatz
|
Vorteile
|
Nachteile
|
Einzelprozess, keine Threads
|
-
Einfachheit
-
Schnelle prozessinterne Nachrichtenübertragung
|
-
Gleichmäßige Lastverteilung nur schwer möglich
-
Skalierung auf mehrere Prozessoren nicht möglich
|
Einzelprozess, mehrere Threads
|
-
Schnelle prozessinterne Nachrichtenübertragung
-
Multitasking ohne Interprozesskommunikation
-
Besseres Multitasking ohne Aufwand für 'Heavyweight'-Prozesse
|
-
Anwendung muss 'Thread-sicher' sein
-
Betriebssystem muss eine effiziente Thread-Verwaltung haben
-
Aspekte in Bezug auf gemeinsam genutzten Speicher müssen berücksichtigt werden.
|
Mehrere Prozesse
|
-
Gute Skalierung beim Hinzufügen von Prozessoren
-
Relativ einfache Verteilung auf Knoten
|
-
Sensibilität bezüglich der Prozessgrenzen: übermäßige Interprozesskommunikation beeinträchtigt
die Leistung
-
Hohe Kosten für Auslagern und Kontextwechsel
-
Schwieriges Design
|
Ein typischer evolutionärer Weg ist, mit einer Einzelprozessarchitektur zu beginnen und anschließend Prozesse für
Verhaltensgruppen hinzuzufügen, die gleichzeitig auftreten müssen. Innerhalb dieser weiter gefassten Gruppierungen
können Sie zusätzliche Parallelitätsanforderungen berücksichtigen und Threads innerhalb von Prozessen hinzufügen, um
die Parallelität zu erhöhen.
Zunächst werden mit einem speziellen Scheduler für aktive Objekte viele aktive Objekte einer einzelnen
Betriebssystem-Task oder einem Betriebssystem-Thread zugeordnet. Auf diese Weise können Sie gewöhnlich eine sehr
schlanke Simulation der Parallelität erreichen, obwohl es mit einer einzelnen Betriebssystem-Task bzw. einem einzelnen
Betriebssystem-Thread nicht möglich sein wird, Maschinen mit mehreren CPUs zu nutzen. Die Schlüsselentscheidung ist,
blockierendes Verhalten in gesonderten Threads zu isolieren, so dass blockierendes Verhalten nicht zu einem Engpass
wird. Dies führt zu einer Absonderung aktiver Objekte mit blockierendem Verhalten in eigene Betriebssystem-Threads.
In Echtzeitsystemen gilt diese Überlegung gleichermaßen für Kapseln. Jede Kapsel hat einen logischen Steuerungs-Thread,
der einen Betriebssystem-Thread, eine Betriebssystem-Task oder einen Betriebssystemprozess mit anderen Kapseln
gemeinsam nutzen kann.
Leider gibt es wie bei vielen Architekturentscheidungen keine einfachen Antworten: Die richtige Lösung erfordert einen
sorgfältig durchdachten Ansatz. Kleine Architekturprototypen können verwendet werden, um die Auswirkungen bestimmter
Auswahlmöglichkeiten zu untersuchen. Konzentrieren Sie sich bei der Erstellung eines Prototyps für die
Prozessarchitektur darauf, die Anzahl der Prozesse hin zu den theoretisch möglichen Maximalwerten für das System zu
skalieren. Berücksichtigen Sie die folgenden Punkte:
-
Kann die Anzahl der Prozesse auf den Maximalwert skaliert werden? Wie weit über diesen Maximalwert hinaus kann das
System ausgereizt werden? Ist Freiraum für ein potenzielles Wachstum vorhanden?
-
Welche Auswirkungen hat es, wenn einige Prozesse in Lightweight-Threads geändert werden, die in einem gemeinsam
genutzten Adressraum arbeiten?
-
Wie entwickeln sich die Antwortzeiten, wenn mehr Prozesse hinzugefügt werden? Hat sich der Umfang der
Interprozesskommunikation (IPC, Inter-Process Communication) erhöht? Ist eine erhebliche Verschlechterung zu
erkennen?
-
Könnte der Umfang der Interprozesskommunikation durch Kombinieren oder Reorganisieren der Prozesse reduziert
werden? Würde eine solche Änderung zu großen monolithischen Prozessen führen, für die ein Lastausgleich nur schwer
realisierbar ist?
-
Kann gemeinsam genutzter Speicher verwendet werden, um die Interprozesskommunikation zu reduzieren?
-
Sollen die Zeitressourcen gleichmäßig auf alle Prozesse verteilt werden? Kann die Zeitzuordnung berücksichtigt
werden? Hat die Änderung der Planungsprioritäten potenzielle Nachteile?
Aktive Objekte können synchron oder asynchron miteinander kommunizieren. Synchrone Kommunikation ist hilfreich, weil
sie komplexe Kollaborationen durch strikt kontrollierte Einhaltung der Nachrichtenreihenfolge vereinfachen kann.
Während ein aktives Objekt einen Schritt von Anfang bis Ende ausführt, der synchrone Aufrufe anderer aktiver Objekte
beinhaltet, können alle parallelen Interaktionen, die von anderen Objekten eingeleitet werden, ignoriert werden, bis
die vollständige Nachrichtenfolge abgeschlossen ist.
Dies kann in manchen Fällen, in anderen wiederum problematisch sein, da es passieren kann, dass ein wichtigeres
Ereignis mit hoher Priorität warten muss (Prioritätsinversion). Dieses Problem kann sich verschlimmern, wenn das
synchron aufgerufene Objekt selbst blockiert ist, weil es auf eine Antwort auf einen eigenen synchronen Aufruf wartet.
Dies kann zu unbegrenzter Prioritätsinversion führen. Wenn die Kette der synchronen Aufrufe eine Schleife enthält, kann
dies im schlimmsten Fall zu einem Deadlock führen.
Asynchrone Aufrufe umgehen dieses Problem, indem sie unbegrenzte Antwortzeiten zulassen. Je nach Softwarearchitektur
führt asynchrone Kommunikation jedoch häufig zu komplexerem Code, da ein aktives Objekt zu einer Zeit möglicherweise
auf mehrere asynchrone Ereignisse antworten muss (die wiederum selbst eine komplexe Abfolge asynchroner Interaktionen
mit anderen aktiven Objekten nach sich ziehen können). Dies kann schwierig zu implementieren und fehleranfällig sein.
Die Verwendung asynchroner Nachrichtenübermittlungstechnologie mit zuverlässiger Nachrichtenübertragung kann die
Anwendungsprogrammierung vereinfachen. Die Anwendung kann ihre Operationen fortsetzen, selbst wenn die Netzverbindung
oder ferne Anwendung nicht verfügbar ist. Asynchrone Nachrichtenübertragung schließt nicht aus, dass sie auch in einem
synchronen Modus verwendet werden kann. Synchrone Technologie setzt voraus, dass eine Verbindung verfügbar ist, wenn
die Anwendung verfügbar ist. Da bekannt ist, dass eine Verbindung vorhanden ist, kann die Handhabung der
COMMIT-Verarbeitung einfacher sein.
In dem in Rational Unified Process für Echtzeitsysteme empfohlenem Ansatz kommunizieren Kapseln asynchron über Signale
auf der Basis bestimmter Protokolle. Trotzdem kann eine synchrone Kommunikation erreicht
werden, indem ein Signal in jede Richtung gesendet wird (Signalpaare).
Obwohl der Aufwand für Kontextwechsel für aktive Objekte sehr gering sein kann, ist es möglich, dass einige Anwendungen
die Kosten trotzdem für inakzeptabel halten. Dies ist gewöhnlich in Situationen der Fall, in denen große Datenmengen
mit hoher Geschwindigkeit verarbeitet werden müssen. In diesen Fällen müssen Sie möglicherweise auf die Verwendung
passiver Objekte und eher traditionelle (aber risikoreicheren) Techniken für Parallelitätsverwaltung zurückgreifen, z.
B. auf Semaphore.
Dies impliziert jedoch nicht unbedingt, den Ansatz mit aktiven Objekten völlig fallen zu lassen. Selbst in solch
datenintensiven Anwendungen macht der leistungskritische Abschnitt einen relativ kleinen Teil des Gesamtsystems aus.
Dies bedeutet, dass der Rest des Systems trotzdem die Vorteile des Konzepts aktiver Objekte nutzen kann.
Im Allgemeinen ist die Leistung nur eines der Designkriterien beim Systemdesign. Wenn das System komplex ist, sind
Kriterien wie Wartungsfreundlichkeit, einfache Änderungsmöglichkeit, Verständlichkeit usw. genauso wichtig, wenn nicht
gar wichtiger. Der Ansatz mit aktiven Objekten hat klare Vorteile, da er einen Großteil der Komplexität von
Parallelität und Parallelitätsverwaltung verbirgt, aber gleichzeitig erlaubt, das Design mit anwendungsspezifischen
Begriffen zu beschreiben und keine technologiespezifischen Mechanismen auf niedriger Ebene voraussetzt.
Parallele Komponenten ohne Interaktionen sind ein nahezu triviales Problem. Fast alle Designherausforderungen haben
etwas mit Interaktionen zwischen gleichzeitig ausgeführten Tasks zu tun. Deshalb müssen wir unsere Energie zunächst auf
das Verständnis der Interaktionen konzentrieren. Eine der Fragen, die Sie stellen müssen, sind:
-
Ist die Interaktion unidirektional, bidirektional oder mehrdirektional?
-
Gibt es eine Client/Server- oder Master/Slave-Beziehung?
-
Ist eine Form der Synchronisation erforderlich?
Nachdem wir die Interaktionen verstanden haben, können wir darüber nachdenken, wie wir sie implementieren. Die
Implementierung muss so gewählt werden, dass sie das einfachste Design ergibt, das den Leistungszielen des Systems
entspricht. Zu den Leistungsanforderungen gehören im Allgemeinen der Gesamtdurchsatz und eine akzeptable Latenzzeit
beim Antworten auf extern generierte Ereignisse.
Diese Aspekte sind bei Echtzeitproblemen noch viel kritischer, weil Echtzeitsysteme weniger tolerant gegenüber
Leistungsschwankungen sind, z. B. gegenüber Abweichungen in der Antwortzeit oder verpassten Terminen.
Es ist davon abzuraten, bestimmte Annahmen über externe Schnittstellen in einer Anwendung zu integrieren, und extrem
ineffizient, mehrere blockierte Steuerungs-Threads zu haben, die auf ein Ereignis warten. Ordnen Sie stattdessen der
jeweiligen Task, die das Ereignis erkennt, ein einzelnes Objekt zu. Wenn das Ereignis eintritt, kann dieses Objekt alle
anderen informieren, die über das Ereignis Bescheid wissen müssen. Dieses Design basiert auf dem anerkannten und
gewährten Designmuster "Beobachter" [GAM94]. Dieses Muster
kann problemlos erweitert werden, um dem "Veröffentlichungskomponente-Subskribent-Muster" eine noch höhere Flexibilität
zu geben, in dem ein Veröffentlichungskomponentenobjekt als Zwischenstation zwischen den Ereignisdetektoren und den
Objekten auftritt, die an dem Ereignis interessiert sind ("Subskribenten") [BUS96].
Aktionen in einem System können durch das Auftreten extern generierter Ereignisse ausgelöst werden. Ein sehr wichtiges
extern generiertes Ereignis kann einfach das Vergehen von Zeit selbst sein, das durch den Sekundentakt einer Uhr
dargestellt wird. Weitere externe Ereignisse stammen von Eingabeeinheiten, die mit externer Hardware verbunden sind,
darunter Benutzerschnittstelleneinheiten, Prozesssensoren und Kommunikationsverbindungen zu anderen Systemen. Dies
trifft in großem Umfang auf Echtzeitsysteme zu, die typischerweise eine hohe Konnektivität zur Außenwelt haben.
Damit Software ein Ereignis erkennen kann, muss sie entweder in der Zeit, in der sie auf einen Interrupt wartet,
blockiert werden oder in regelmäßigen Abständen bei der Hardware anfragen, ob das Ereignis eingetreten ist. Im letzeren
Fall muss der Abfragezyklus unter Umständen kurz sein, um kurzlebige Ereignisse oder Mehrfachvorkommen nicht zu
verpassen oder um einfach die Latenzzeit zwischen Ereigniseintritt und Ereigniserkennung zu minimieren.
Der interessante Aspekt ist hier, dass auch bei selten eintretenden Ereignissen irgendeine Software blockiert werden
muss, die auf das Ereignis wartet oder das Ereignis häufig prüft. Viele (wenn nicht gar die meisten) Ereignisse, die
ein System behandeln muss, treten jedoch nur selten ein. Die meiste Zeit passiert in den Systemen nichts von Bedeutung.
Das Aufzugsystem liefert hierfür viele gute Beispiele. Zu den wichtigen Ereignissen im Leben eines Aufzugs gehören das
Anfordern eines Aufzugs, die Auswahl des Stockwerks, das Blockieren der Aufzugtür durch einen Passagier und das Fahren
von einem Stockwerk zum nächsten. Einige dieser Ereignisse erfordern sehr zeitkritische Antworten, aber alle Ereignisse
treten im Vergleich mit der Zeitskala der gewünschten Antwortzeit äußerst selten auf.
Ein einzelnes Ereignis kann viele Aktionen auslösen, und die Aktionen können von den Zuständen verschiedener Objekte
abhängen. Außerdem können verschiedene Konfigurationen eine Systems dasselbe Ereignis unterschiedlich verwenden. Wenn
ein Aufzug beispielsweise ein Stockwerk passiert, muss die Anzeige in der Aufzugkabine aktualisiert werden, und der
Aufzug selbst muss wissen, wo er ist, damit er in der Lage ist, auf eine neue Aufrufanforderung und eine neue
Stockwerkauswahl zu reagieren. In jedem Stockwerk kann es Aufzugpositionsanzeigen geben.
Abfragen (Polling) sind kostenintensiv. Bei Abfragen muss ein Teil des Systems regelmäßig seine Arbeit unterbrechen, um
zu prüfen, ob ein Ereignis eingetreten ist. Ist eine schnelle Reaktion auf das Ereignis erforderlich, muss das System
in kurzen Zeitabständen prüfen, ob das Ereignis vorliegt, was den Umfang der anderen Arbeiten, die ausgeführt werden
könnten, weiter einschränkt.
Es ist wesentlich effizienter, dem Ereignis einen Interrupt zuzuordnen, so dass der ereignisabhängige Code durch den
Interrupt aktiviert wird. Obwohl Interrupts manchmal vermieden werden, weil sie als "kostenintensiv" gelten, kann eine
durchdachte Verwendung von Interrupts wesentlich effizienter sein als wiederholte Abfragen.
Interrupts als Ereignisbenachrichtigungmechanismen werden in solchen Fällen bevorzugt, in denen der Ereigniseintritt
zufällig und selten ist und die meisten Abfragen nur feststellen würden, dass das Ereignis nicht eingetreten ist.
Abfragen werden in den Fällen bevorzugt, in denen die Ereignisse in regelmäßigen und vorhersagbaren Intervallen
eintreten und in denen die meisten Abfragen feststellen würden, dass das Ereignis eingetreten ist. Dazwischen gibt es
Fälle, in denen es egal ist, ob Abfrage- oder reaktives Verhalten verwendet wird, und in denen beide Mechanismen
gleichermaßen geeignet sind. Aufgrund der Zufälligkeit von Ereignissen in der realen Welt wird jedoch das reaktive
Verhalten meistens bevorzugt.
Das Rundsenden von Daten (normalerweise mit Signalen) ist kostenintensiv und in gewöhnlich verschwenderisch.
Möglicherweise sind nur wenige Objekte an den Daten interessiert, aber jedes (oder viele) muss seine Arbeit
unterbrechen, um die Daten zu überprüfen. Ein besserer, weniger ressourcenintensiver Ansatz ist die Verwendung von
Benachrichtigungen, so dass nur die Objekte informiert werden, die sich dafür interessieren, dass das Ereignis
eingetreten ist. Beschränken Sie das Rundsenden auf Ereignisse, die die Aufmerksamkeit vieler Objekte erfordern
(gewöhnlich Zeit- oder Synchronisationsereignisse).
Dies bedeutet im Einzelnen:
-
Verwenden Sie passive Objekte und synchrone Methodenaufrufe, wenn Parallelität kein Thema ist, sondern
unverzügliche Antwort.
-
Verwenden Sie aktive Objekte und asynchrone Nachrichten für die große Mehrzahl von Parallelitätskonzepten auf
Anwendungsebene.
-
Verwenden Sie Betriebssystem-Threads, um blockierende Elemente zu isolieren. Ein aktives Objekt kann einem
Betriebssystem-Thread zugeordnet werden.
-
Verwenden Sie Betriebssystemprozesse für maximale Isolation. Es sind separate Prozesse erforderlich, wenn Programme
unabhängig voneinander gestartet und beendet werden müssen, und für Subsysteme, die verteilt werden müssen.
-
Verwenden Sie separate CPUs für die physische Verteilung oder für die reine Leistung.
Die vielleicht wichtigste Richtlinie für die Entwicklung effizienter paralleler Anwendungen ist eine maximale
Verwendung von Lightweight-Parallelitätsmechanismen. Hardware und Betriebssystemsoftware spielen eine wichtige Rolle
bei der Unterstützung von Parallelität, aber beide bieten eher Heavyweight-Mechanismen an, die dem Anwendungsdesigner
einen Großteil der Arbeit überlassen. Wir müssen eine große Lücke zwischen den verfügbaren Tools und den Bedürfnissen
paralleler Anwendungen schließen.
Die beiden Schlüssel-Features aktiver Objekte helfen, diese Lücke zu schließen:
-
Sie vereinheitlichen die Designabstraktionen, indem Sie die Basiseinheit der Parallelität (einen Steuerungs-Thread)
kapseln, der mit einem der vom Betriebssystem oder von der CPU bereitgestellten Mechanismen implementiert werden
kann.
-
Wenn aktive Objekte einen Betriebssystem-Thread gemeinsam nutzen, werden sie zu einem sehr effizienten, schlanken
Parallelitätsmechanismus, der ansonsten direkt in der Anwendung implementiert werden müsste.
Aktive Objekte können auch eine ideale Umgebung für die passiven Objekte sein, die von Programmiersprachen
bereitgestellt werden. Wenn Sie ein System vollständig auf der Grundlage paralleler Objekte ohne prozedurorientierte
Artefakte wie Programme und Prozesse entwerfen, erhalten Sie ein modulares, kohäsives und verständliches Design.
In den meisten Systemen verwendet weniger als 10 % des Codes mehr als 90 % der CPU-Zyklen.
Viele Systemdesigner meinen, es müsste jede Codezeile optimiert werden. Stattdessen sollten Sie lieber die 10 % des
Codes optimieren, der am häufigsten und am längsten ausgeführt wird. Entwerfen Sie die anderen 90 % mit Schwerpunkt auf
Verständlichkeit, Wartungsfreundlichkeit, Modularität und einfache Implementierung.
Die nicht funktionalen Anforderungen und die Architektur des Systems wirken sich auf die Auswahl der Mechanismen aus,
die für die Implementierung ferner Prozeduraufrufe verwendet werden. Eine Übersicht über die Arten von Abwägungen
zwischen den Alternativen finden Sie im Folgenden:
Mechanismus
|
Verwendung
|
Kommentare
|
Nachrichtenübertragung
|
Asynchroner Zugriff auf Enterprise-Server
|
Middleware für Nachrichtenübertragung kann die Anwendungsprogrammierung erheblich vereinfachen, indem
sie die Steuerung der Warteschlange, Zeitlimitüberschreitungen und
Wiederherstellungs-/Neustartbedingungen behandelt. Sie können Middleware für Nachrichtenübertragung
auch in einem pseudosynchronen Modus verwenden. Messaging-Technologie kann gewöhnlich große
Nachrichtenlängen unterstützen. Einige RPC-Ansätze sind bezüglich der Nachrichtenlänge möglicherweise
eingeschränkt und erfordern zusätzliche Programmierarbeiten für die Behandlung großer Nachrichten.
|
JDBC/ODBC
|
Datenbankaufrufe
|
Dies sind datenbankunabhängige Schnittstellen, damit Java-Servlets oder -Anwendungsprogramme Aufrufe an
Datenbanken absetzen können, die sich auf demselben oder einem anderen Server befinden.
|
Native Schnittstellen
|
Datenbankaufrufe
|
Viele Datenbanklieferanten haben native Anwendungsprogrammschnittstellen zu ihren eigenen Datenbanken
implementiert, die im Vergleich mit ODBC einen Leistungsvorteil bieten, was jedoch zu Lasten der
Portierbarkeit der Anwendung geht.
|
Fernprozeduraufruf
|
Zum Aufruf von Programmen auf fernen Servern
|
Möglicherweise müssen Sie nicht auf RPC-Ebene programmieren, wenn Sie ein Anwendungserstellungsprogramm
haben, das dies für Sie übernimmt.
|
Interaktiv
|
In e-business-Anwendungen selten verwendet
|
Typischerweise Kommunikation zwischen Programmen auf niedriger Ebene über Protokolle wie APPC oder
Sockets.
|
Viele Systeme erfordern paralleles Verhalten und verteilte Komponenten. Die meisten Programmiersprachen geben uns wenig
Hilfestellung zu diesen Problemen. Die Erfahrung hat gezeigt, dass gute Abstraktionen erforderlich sind, um den Bedarf
an Parallelität in Anwendungen und die Optionen für die Implementierung von Parallelität in Software zu verstehen. Wir
haben auch festgestellt, dass parallele Software tendenziell zwar komplexer als nicht parallele Software ist, aber
trotzdem in der Lage ist, das Design von Systemen, die mit Parallelität in der realen Welt umgehen müssen, erheblich zu
vereinfachen.
|